chore: add vmess, shadowsocks, tcptun and udptun listener

This commit is contained in:
wwqgtxx 2022-11-11 20:56:08 +08:00
parent 6dadc2357a
commit 3eacce9a66
10 changed files with 796 additions and 58 deletions

View file

@ -60,15 +60,19 @@ type General struct {
// Inbound config
type Inbound struct {
Port int `json:"port"`
SocksPort int `json:"socks-port"`
RedirPort int `json:"redir-port"`
TProxyPort int `json:"tproxy-port"`
MixedPort int `json:"mixed-port"`
Authentication []string `json:"authentication"`
AllowLan bool `json:"allow-lan"`
BindAddress string `json:"bind-address"`
InboundTfo bool `json:"inbound-tfo"`
Port int `json:"port"`
SocksPort int `json:"socks-port"`
RedirPort int `json:"redir-port"`
TProxyPort int `json:"tproxy-port"`
MixedPort int `json:"mixed-port"`
ShadowSocksConfig string `json:"ss-config"`
VmessConfig string `json:"vmess-config"`
TcpTunConfig string `json:"tcptun-config"`
UdpTunConfig string `json:"udptun-config"`
Authentication []string `json:"authentication"`
AllowLan bool `json:"allow-lan"`
BindAddress string `json:"bind-address"`
InboundTfo bool `json:"inbound-tfo"`
}
// Controller config
@ -284,6 +288,10 @@ type RawConfig struct {
RedirPort int `yaml:"redir-port"`
TProxyPort int `yaml:"tproxy-port"`
MixedPort int `yaml:"mixed-port"`
ShadowSocksConfig string `yaml:"ss-config"`
VmessConfig string `yaml:"vmess-config"`
TcpTunConfig string `yaml:"tcptun-config"`
UdpTunConfig string `yaml:"udptun-config"`
InboundTfo bool `yaml:"inbound-tfo"`
Authentication []string `yaml:"authentication"`
AllowLan bool `yaml:"allow-lan"`
@ -526,14 +534,18 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun
return &General{
Inbound: Inbound{
Port: cfg.Port,
SocksPort: cfg.SocksPort,
RedirPort: cfg.RedirPort,
TProxyPort: cfg.TProxyPort,
MixedPort: cfg.MixedPort,
AllowLan: cfg.AllowLan,
BindAddress: cfg.BindAddress,
InboundTfo: cfg.InboundTfo,
Port: cfg.Port,
SocksPort: cfg.SocksPort,
RedirPort: cfg.RedirPort,
TProxyPort: cfg.TProxyPort,
MixedPort: cfg.MixedPort,
ShadowSocksConfig: cfg.ShadowSocksConfig,
VmessConfig: cfg.VmessConfig,
TcpTunConfig: cfg.TcpTunConfig,
UdpTunConfig: cfg.UdpTunConfig,
AllowLan: cfg.AllowLan,
BindAddress: cfg.BindAddress,
InboundTfo: cfg.InboundTfo,
},
Controller: Controller{
ExternalController: cfg.ExternalController,

View file

@ -20,8 +20,12 @@ const (
HTTPS
SOCKS4
SOCKS5
SHADOWSOCKS
VMESS
REDIR
TPROXY
TCPTUN
UDPTUN
TUN
INNER
)
@ -53,10 +57,18 @@ func (t Type) String() string {
return "Socks4"
case SOCKS5:
return "Socks5"
case SHADOWSOCKS:
return "ShadowSocks"
case VMESS:
return "Vmess"
case REDIR:
return "Redir"
case TPROXY:
return "TProxy"
case TCPTUN:
return "TcpTun"
case UDPTUN:
return "UdpTun"
case TUN:
return "Tun"
case INNER:

View file

@ -105,14 +105,18 @@ func GetGeneral() *config.General {
general := &config.General{
Inbound: config.Inbound{
Port: ports.Port,
SocksPort: ports.SocksPort,
RedirPort: ports.RedirPort,
TProxyPort: ports.TProxyPort,
MixedPort: ports.MixedPort,
Authentication: authenticator,
AllowLan: P.AllowLan(),
BindAddress: P.BindAddress(),
Port: ports.Port,
SocksPort: ports.SocksPort,
RedirPort: ports.RedirPort,
TProxyPort: ports.TProxyPort,
MixedPort: ports.MixedPort,
ShadowSocksConfig: ports.ShadowSocksConfig,
VmessConfig: ports.VmessConfig,
TcpTunConfig: ports.TcpTunConfig,
UdpTunConfig: ports.UdpTunConfig,
Authentication: authenticator,
AllowLan: P.AllowLan(),
BindAddress: P.BindAddress(),
},
Mode: tunnel.Mode(),
LogLevel: log.Level(),
@ -342,6 +346,10 @@ func updateGeneral(general *config.General, force bool) {
P.ReCreateAutoRedir(general.EBpf.AutoRedir, tcpIn, udpIn)
P.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn)
P.ReCreateMixed(general.MixedPort, tcpIn, udpIn)
P.ReCreateShadowSocks(general.ShadowSocksConfig, tcpIn, udpIn)
P.ReCreateVmess(general.VmessConfig, tcpIn, udpIn)
P.ReCreateTcpTun(general.TcpTunConfig, tcpIn, udpIn)
P.ReCreateUdpTun(general.UdpTunConfig, tcpIn, udpIn)
}
func updateUsers(users []auth.AuthUser) {

View file

@ -35,20 +35,24 @@ func configRouter() http.Handler {
}
type configSchema struct {
Port *int `json:"port"`
SocksPort *int `json:"socks-port"`
RedirPort *int `json:"redir-port"`
TProxyPort *int `json:"tproxy-port"`
MixedPort *int `json:"mixed-port"`
Tun *tunSchema `json:"tun"`
AllowLan *bool `json:"allow-lan"`
BindAddress *string `json:"bind-address"`
Mode *tunnel.TunnelMode `json:"mode"`
LogLevel *log.LogLevel `json:"log-level"`
IPv6 *bool `json:"ipv6"`
Sniffing *bool `json:"sniffing"`
TcpConcurrent *bool `json:"tcp-concurrent"`
InterfaceName *string `json:"interface-name"`
Port *int `json:"port"`
SocksPort *int `json:"socks-port"`
RedirPort *int `json:"redir-port"`
TProxyPort *int `json:"tproxy-port"`
MixedPort *int `json:"mixed-port"`
Tun *tunSchema `json:"tun"`
ShadowSocksConfig *string `json:"ss-config"`
VmessConfig *string `json:"vmess-config"`
TcptunConfig *string `json:"tcptun-config"`
UdptunConfig *string `json:"udptun-config"`
AllowLan *bool `json:"allow-lan"`
BindAddress *string `json:"bind-address"`
Mode *tunnel.TunnelMode `json:"mode"`
LogLevel *log.LogLevel `json:"log-level"`
IPv6 *bool `json:"ipv6"`
Sniffing *bool `json:"sniffing"`
TcpConcurrent *bool `json:"tcp-concurrent"`
InterfaceName *string `json:"interface-name"`
}
type tunSchema struct {
@ -90,6 +94,14 @@ func pointerOrDefault(p *int, def int) int {
return def
}
func pointerOrDefaultString(p *string, def string) string {
if p != nil {
return *p
}
return def
}
func pointerOrDefaultTun(p *tunSchema, def config.Tun) config.Tun {
if p != nil {
def.Enable = p.Enable
@ -187,6 +199,10 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn)
P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn)
P.ReCreateTun(pointerOrDefaultTun(general.Tun, P.LastTunConf), tcpIn, udpIn)
P.ReCreateShadowSocks(pointerOrDefaultString(general.ShadowSocksConfig, ports.ShadowSocksConfig), tcpIn, udpIn)
P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tcpIn, udpIn)
P.ReCreateTcpTun(pointerOrDefaultString(general.TcptunConfig, ports.TcpTunConfig), tcpIn, udpIn)
P.ReCreateUdpTun(pointerOrDefaultString(general.UdptunConfig, ports.UdpTunConfig), tcpIn, udpIn)
if general.Mode != nil {
tunnel.SetMode(*general.Mode)

View file

@ -2,7 +2,6 @@ package proxy
import (
"fmt"
"github.com/Dreamacro/clash/listener/sing_tun"
"golang.org/x/exp/slices"
"net"
"sort"
@ -18,8 +17,12 @@ import (
"github.com/Dreamacro/clash/listener/inner"
"github.com/Dreamacro/clash/listener/mixed"
"github.com/Dreamacro/clash/listener/redir"
"github.com/Dreamacro/clash/listener/sing_shadowsocks"
"github.com/Dreamacro/clash/listener/sing_tun"
"github.com/Dreamacro/clash/listener/sing_vmess"
"github.com/Dreamacro/clash/listener/socks"
"github.com/Dreamacro/clash/listener/tproxy"
"github.com/Dreamacro/clash/listener/tunnel"
"github.com/Dreamacro/clash/log"
)
@ -28,19 +31,23 @@ var (
bindAddress = "*"
inboundTfo = false
socksListener *socks.Listener
socksUDPListener *socks.UDPListener
httpListener *http.Listener
redirListener *redir.Listener
redirUDPListener *tproxy.UDPListener
tproxyListener *tproxy.Listener
tproxyUDPListener *tproxy.UDPListener
mixedListener *mixed.Listener
mixedUDPLister *socks.UDPListener
tunLister *sing_tun.Listener
autoRedirListener *autoredir.Listener
autoRedirProgram *ebpf.TcEBpfProgram
tcProgram *ebpf.TcEBpfProgram
socksListener *socks.Listener
socksUDPListener *socks.UDPListener
httpListener *http.Listener
redirListener *redir.Listener
redirUDPListener *tproxy.UDPListener
tproxyListener *tproxy.Listener
tproxyUDPListener *tproxy.UDPListener
mixedListener *mixed.Listener
mixedUDPLister *socks.UDPListener
tunLister *sing_tun.Listener
shadowSocksListener *sing_shadowsocks.Listener
vmessListener *sing_vmess.Listener
tcpTunListener *tunnel.Listener
udpTunListener *tunnel.UdpListener
autoRedirListener *autoredir.Listener
autoRedirProgram *ebpf.TcEBpfProgram
tcProgram *ebpf.TcEBpfProgram
// lock for recreate function
socksMux sync.Mutex
@ -49,6 +56,10 @@ var (
tproxyMux sync.Mutex
mixedMux sync.Mutex
tunMux sync.Mutex
ssMux sync.Mutex
vmessMux sync.Mutex
tcpTunMux sync.Mutex
udpTunMux sync.Mutex
autoRedirMux sync.Mutex
tcMux sync.Mutex
@ -56,11 +67,15 @@ var (
)
type Ports struct {
Port int `json:"port"`
SocksPort int `json:"socks-port"`
RedirPort int `json:"redir-port"`
TProxyPort int `json:"tproxy-port"`
MixedPort int `json:"mixed-port"`
Port int `json:"port"`
SocksPort int `json:"socks-port"`
RedirPort int `json:"redir-port"`
TProxyPort int `json:"tproxy-port"`
MixedPort int `json:"mixed-port"`
ShadowSocksConfig string `json:"ss-config"`
VmessConfig string `json:"vmess-config"`
TcpTunConfig string `json:"tcptun-config"`
UdpTunConfig string `json:"udptun-config"`
}
func GetTunConf() config.Tun {
@ -235,6 +250,156 @@ func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P
log.Infoln("Redirect proxy listening at: %s", redirListener.Address())
}
func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
ssMux.Lock()
defer ssMux.Unlock()
var err error
defer func() {
if err != nil {
log.Errorln("Start ShadowSocks server error: %s", err.Error())
}
}()
shouldIgnore := false
if shadowSocksListener != nil {
if shadowSocksListener.Config() != shadowSocksConfig {
shadowSocksListener.Close()
shadowSocksListener = nil
} else {
shouldIgnore = true
}
}
if shouldIgnore {
return
}
if len(shadowSocksConfig) == 0 {
return
}
listener, err := sing_shadowsocks.New(shadowSocksConfig, tcpIn, udpIn)
if err != nil {
return
}
shadowSocksListener = listener
return
}
func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
vmessMux.Lock()
defer vmessMux.Unlock()
var err error
defer func() {
if err != nil {
log.Errorln("Start Vmess server error: %s", err.Error())
}
}()
shouldIgnore := false
if vmessListener != nil {
if vmessListener.Config() != vmessConfig {
vmessListener.Close()
vmessListener = nil
} else {
shouldIgnore = true
}
}
if shouldIgnore {
return
}
if len(vmessConfig) == 0 {
return
}
listener, err := sing_vmess.New(vmessConfig, tcpIn, udpIn)
if err != nil {
return
}
vmessListener = listener
return
}
func ReCreateTcpTun(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
tcpTunMux.Lock()
defer tcpTunMux.Unlock()
shouldIgnore := false
var err error
defer func() {
if err != nil {
log.Errorln("Start TcpTun server error: %s", err.Error())
}
}()
if tcpTunListener != nil {
if tcpTunListener.Config() != config {
tcpTunListener.Close()
tcpTunListener = nil
} else {
shouldIgnore = true
}
}
if shouldIgnore {
return
}
tcpListener, err := tunnel.New(config, tcpIn)
if err != nil {
return
}
tcpTunListener = tcpListener
return
}
func ReCreateUdpTun(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
udpTunMux.Lock()
defer udpTunMux.Unlock()
shouldIgnore := false
var err error
defer func() {
if err != nil {
log.Errorln("Start UdpTun server error: %s", err.Error())
}
}()
if udpTunListener != nil {
if udpTunListener.Config() != config {
udpTunListener.Close()
udpTunListener = nil
} else {
shouldIgnore = true
}
}
if shouldIgnore {
return
}
udpListener, err := tunnel.NewUdp(config, udpIn)
if err != nil {
return
}
udpTunListener = udpListener
return
}
func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
tproxyMux.Lock()
defer tproxyMux.Unlock()
@ -490,6 +655,22 @@ func GetPorts() *Ports {
ports.MixedPort = port
}
if shadowSocksListener != nil {
ports.ShadowSocksConfig = shadowSocksListener.Config()
}
if vmessListener != nil {
ports.VmessConfig = vmessListener.Config()
}
if tcpTunListener != nil {
ports.TcpTunConfig = tcpTunListener.Config()
}
if udpTunListener != nil {
ports.UdpTunConfig = udpTunListener.Config()
}
return ports
}

View file

@ -0,0 +1,173 @@
package sing_shadowsocks
import (
"context"
"fmt"
"net"
"net/url"
"strings"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/sockopt"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/sing"
"github.com/Dreamacro/clash/log"
shadowsocks "github.com/sagernet/sing-shadowsocks"
"github.com/sagernet/sing-shadowsocks/shadowaead"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/metadata"
)
type Listener struct {
closed bool
config string
listeners []net.Listener
udpListeners []net.PacketConn
service shadowsocks.Service
}
var _listener *Listener
func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) {
addr, cipher, password, err := parseSSURL(config)
if err != nil {
return nil, err
}
udpTimeout := int64(sing.UDPTimeout.Seconds())
h := &sing.ListenerHandler{
TcpIn: tcpIn,
UdpIn: udpIn,
Type: C.SHADOWSOCKS,
}
sl := &Listener{false, config, nil, nil, nil}
switch {
case 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)
default:
err = fmt.Errorf("shadowsocks: unsupported method: %s", cipher)
}
if err != nil {
return nil, err
}
_listener = sl
for _, addr := range strings.Split(addr, ",") {
addr := addr
//UDP
ul, err := net.ListenPacket("udp", addr)
if err != nil {
return nil, err
}
err = sockopt.UDPReuseaddr(ul.(*net.UDPConn))
if err != nil {
log.Warnln("Failed to Reuse UDP Address: %s", err)
}
sl.udpListeners = append(sl.udpListeners, ul)
go func() {
conn := bufio.NewPacketConn(ul)
for {
buff := buf.NewPacket()
remoteAddr, err := conn.ReadPacket(buff)
if err != nil {
buff.Release()
if sl.closed {
break
}
continue
}
_ = sl.service.NewPacket(context.TODO(), conn, buff, metadata.Metadata{
Protocol: "shadowsocks",
Source: remoteAddr,
})
}
}()
//TCP
l, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
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 {
if sl.closed {
break
}
continue
}
_ = c.(*net.TCPConn).SetKeepAlive(true)
go sl.HandleConn(c)
}
}()
}
return sl, nil
}
func (l *Listener) Close() {
l.closed = true
for _, lis := range l.listeners {
_ = lis.Close()
}
for _, lis := range l.udpListeners {
_ = lis.Close()
}
}
func (l *Listener) Config() string {
return l.config
}
func (l *Listener) HandleConn(conn net.Conn) {
err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{
Protocol: "shadowsocks",
Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()),
})
if err != nil {
_ = conn.Close()
return
}
}
func HandleShadowSocks(conn net.Conn) bool {
if _listener != nil && _listener.service != nil {
go _listener.HandleConn(conn)
return true
}
return false
}
func parseSSURL(s string) (addr, cipher, password string, err error) {
u, err := url.Parse(s)
if err != nil {
return
}
addr = u.Host
if u.User != nil {
cipher = u.User.Username()
password, _ = u.User.Password()
}
return
}

View file

@ -0,0 +1,126 @@
package sing_vmess
import (
"context"
"net"
"net/url"
"strings"
"github.com/Dreamacro/clash/adapter/inbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/sing"
"github.com/Dreamacro/clash/log"
vmess "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common/metadata"
)
type Listener struct {
closed bool
config string
listeners []net.Listener
service *vmess.Service[string]
}
var _listener *Listener
func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) {
addr, username, password, err := parseVmessURL(config)
if err != nil {
return nil, err
}
h := &sing.ListenerHandler{
TcpIn: tcpIn,
UdpIn: udpIn,
Type: C.VMESS,
}
service := vmess.NewService[string](h)
err = service.UpdateUsers([]string{username}, []string{password}, []int{1})
if err != nil {
return nil, err
}
err = service.Start()
if err != nil {
return nil, err
}
sl := &Listener{false, config, nil, service}
_listener = sl
for _, addr := range strings.Split(addr, ",") {
addr := addr
//TCP
l, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
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 {
if sl.closed {
break
}
continue
}
_ = c.(*net.TCPConn).SetKeepAlive(true)
go sl.HandleConn(c)
}
}()
}
return sl, nil
}
func (l *Listener) Close() {
l.closed = true
for _, lis := range l.listeners {
_ = lis.Close()
}
_ = l.service.Close()
}
func (l *Listener) Config() string {
return l.config
}
func (l *Listener) HandleConn(conn net.Conn) {
err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{
Protocol: "vmess",
Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()),
})
if err != nil {
_ = conn.Close()
return
}
}
func HandleVmess(conn net.Conn) bool {
if _listener != nil && _listener.service != nil {
go _listener.HandleConn(conn)
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
}

68
listener/tunnel/tcp.go Normal file
View file

@ -0,0 +1,68 @@
package tunnel
import (
"net"
"github.com/Dreamacro/clash/adapter/inbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5"
)
type Listener struct {
closed bool
config string
listeners []net.Listener
}
func New(config string, in chan<- C.ConnContext) (*Listener, error) {
tl := &Listener{false, config, nil}
pl := PairList{}
err := pl.Set(config)
if err != nil {
return nil, err
}
for _, p := range pl {
addr := p[0]
target := p[1]
go func() {
tgt := socks5.ParseAddr(target)
if tgt == nil {
log.Errorln("invalid target address %q", target)
return
}
l, err := net.Listen("tcp", addr)
if err != nil {
return
}
tl.listeners = append(tl.listeners, l)
log.Infoln("TCP tunnel %s <-> %s", l.Addr().String(), target)
for {
c, err := l.Accept()
if err != nil {
if tl.closed {
break
}
continue
}
_ = c.(*net.TCPConn).SetKeepAlive(true)
in <- inbound.NewSocket(tgt, c, C.TCPTUN)
}
}()
}
return tl, nil
}
func (l *Listener) Close() {
l.closed = true
for _, lis := range l.listeners {
_ = lis.Close()
}
}
func (l *Listener) Config() string {
return l.config
}

79
listener/tunnel/udp.go Normal file
View file

@ -0,0 +1,79 @@
package tunnel
import (
"net"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5"
)
type UdpListener struct {
closed bool
config string
listeners []net.PacketConn
}
func NewUdp(config string, in chan<- *inbound.PacketAdapter) (*UdpListener, error) {
ul := &UdpListener{false, config, nil}
pl := PairList{}
err := pl.Set(config)
if err != nil {
return nil, err
}
for _, p := range pl {
addr := p[0]
target := p[1]
go func() {
tgt := socks5.ParseAddr(target)
if tgt == nil {
log.Errorln("invalid target address %q", target)
return
}
l, err := net.ListenPacket("udp", addr)
if err != nil {
return
}
ul.listeners = append(ul.listeners, l)
log.Infoln("Udp tunnel %s <-> %s", l.LocalAddr().String(), target)
for {
buf := pool.Get(pool.RelayBufferSize)
n, remoteAddr, err := l.ReadFrom(buf)
if err != nil {
pool.Put(buf)
if ul.closed {
break
}
continue
}
packet := &packet{
pc: l,
rAddr: remoteAddr,
payload: buf[:n],
bufRef: buf,
}
select {
case in <- inbound.NewPacket(tgt, packet, C.UDPTUN):
default:
}
}
}()
}
return ul, nil
}
func (l *UdpListener) Close() {
l.closed = true
for _, lis := range l.listeners {
_ = lis.Close()
}
}
func (l *UdpListener) Config() string {
return l.config
}

63
listener/tunnel/utils.go Normal file
View file

@ -0,0 +1,63 @@
package tunnel
import (
"errors"
"net"
"strings"
"github.com/Dreamacro/clash/common/pool"
)
type PairList [][2]string // key1=val1,key2=val2,...
func (l PairList) String() string {
s := make([]string, len(l))
for i, pair := range l {
s[i] = pair[0] + "=" + pair[1]
}
return strings.Join(s, ",")
}
func (l *PairList) Set(s string) error {
for _, item := range strings.Split(s, ",") {
pair := strings.Split(item, "=")
if len(pair) != 2 {
return nil
}
*l = append(*l, [2]string{pair[0], pair[1]})
}
return nil
}
type packet struct {
pc net.PacketConn
rAddr net.Addr
payload []byte
bufRef []byte
}
func (c *packet) Data() []byte {
return c.payload
}
// WriteBack wirtes UDP packet with source(ip, port) = `addr`
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
if addr == nil {
err = errors.New("address is invalid")
return
}
packet := b
return c.pc.WriteTo(packet, c.rAddr)
}
// LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr {
return c.rAddr
}
func (c *packet) Drop() {
pool.Put(c.bufRef)
}
func (c *packet) InAddr() net.Addr {
return c.pc.LocalAddr()
}