Merge branch 'Alpha' into Meta
This commit is contained in:
commit
070f8f8949
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -8,13 +8,11 @@ on:
|
|||||||
- ".github/ISSUE_TEMPLATE/**"
|
- ".github/ISSUE_TEMPLATE/**"
|
||||||
branches:
|
branches:
|
||||||
- Alpha
|
- Alpha
|
||||||
- Meta
|
|
||||||
tags:
|
tags:
|
||||||
- "v*"
|
- "v*"
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
branches:
|
branches:
|
||||||
- Alpha
|
- Alpha
|
||||||
- Meta
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.ref }}-${{ github.workflow }}
|
group: ${{ github.ref }}-${{ github.workflow }}
|
||||||
|
@ -4,16 +4,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Dreamacro/clash/common/queue"
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/atomic"
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
|
"github.com/Dreamacro/clash/common/queue"
|
||||||
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
var UnifiedDelay = atomic.NewBool(false)
|
var UnifiedDelay = atomic.NewBool(false)
|
||||||
|
@ -3,9 +3,9 @@ package outbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
N "github.com/Dreamacro/clash/common/net"
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
"github.com/Dreamacro/clash/common/utils"
|
"github.com/Dreamacro/clash/common/utils"
|
||||||
@ -47,31 +47,31 @@ func (b *Base) Type() C.AdapterType {
|
|||||||
|
|
||||||
// StreamConn implements C.ProxyAdapter
|
// StreamConn implements C.ProxyAdapter
|
||||||
func (b *Base) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
func (b *Base) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
return c, errors.New("no support")
|
return c, C.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Base) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
func (b *Base) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||||
return nil, errors.New("no support")
|
return nil, C.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContextWithDialer implements C.ProxyAdapter
|
// DialContextWithDialer implements C.ProxyAdapter
|
||||||
func (b *Base) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
func (b *Base) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
return nil, errors.New("no support")
|
return nil, C.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||||
return nil, errors.New("no support")
|
return nil, C.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketWithDialer implements C.ProxyAdapter
|
// ListenPacketWithDialer implements C.ProxyAdapter
|
||||||
func (b *Base) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (b *Base) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
return nil, errors.New("no support")
|
return nil, C.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (b *Base) SupportWithDialer() bool {
|
func (b *Base) SupportWithDialer() C.NetWork {
|
||||||
return false
|
return C.InvalidNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportUOT implements C.ProxyAdapter
|
// SupportUOT implements C.ProxyAdapter
|
||||||
@ -94,6 +94,11 @@ func (b *Base) SupportTFO() bool {
|
|||||||
return b.tfo
|
return b.tfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsL3Protocol implements C.ProxyAdapter
|
||||||
|
func (b *Base) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalJSON implements C.ProxyAdapter
|
// MarshalJSON implements C.ProxyAdapter
|
||||||
func (b *Base) MarshalJSON() ([]byte, error) {
|
func (b *Base) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(map[string]string{
|
return json.Marshal(map[string]string{
|
||||||
@ -146,6 +151,7 @@ type BasicOption struct {
|
|||||||
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
|
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
|
||||||
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
|
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
|
||||||
IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"`
|
IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"`
|
||||||
|
DialerProxy string `proxy:"dialer-proxy,omitempty"` // don't apply this option into groups, but can set a group name in a proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseOption struct {
|
type BaseOption struct {
|
||||||
@ -198,7 +204,18 @@ func (c *conn) Upstream() any {
|
|||||||
return c.ExtendedConn
|
return c.ExtendedConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *conn) WriterReplaceable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) ReaderReplaceable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
|
func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
|
||||||
|
if _, ok := c.(syscall.Conn); !ok { // exclusion system conn like *net.TCPConn
|
||||||
|
c = N.NewDeadlineConn(c) // most conn from outbound can't handle readDeadline correctly
|
||||||
|
}
|
||||||
return &conn{N.NewExtendedConn(c), []string{a.Name()}, parseRemoteDestination(a.Addr())}
|
return &conn{N.NewExtendedConn(c), []string{a.Name()}, parseRemoteDestination(a.Addr())}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,6 +247,9 @@ func (c *packetConn) LocalAddr() net.Addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
|
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
|
||||||
|
if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn
|
||||||
|
pc = N.NewDeadlinePacketConn(pc) // most conn from outbound can't handle readDeadline correctly
|
||||||
|
}
|
||||||
return &packetConn{pc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())}
|
return &packetConn{pc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
@ -26,7 +27,14 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
|
|||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||||
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
|
// net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr
|
||||||
|
if !metadata.Resolved() {
|
||||||
|
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("can't resolve ip")
|
||||||
|
}
|
||||||
|
metadata.DstIP = ip
|
||||||
|
}
|
||||||
pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", metadata.DstIP), "", d.Base.DialOptions(opts...)...)
|
pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", metadata.DstIP), "", d.Base.DialOptions(opts...)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
)
|
)
|
||||||
@ -66,6 +67,12 @@ func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di
|
|||||||
|
|
||||||
// DialContextWithDialer implements C.ProxyAdapter
|
// DialContextWithDialer implements C.ProxyAdapter
|
||||||
func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
|
if len(h.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(h.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", h.addr)
|
c, err := dialer.DialContext(ctx, "tcp", h.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", h.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", h.addr, err)
|
||||||
@ -85,8 +92,8 @@ func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metad
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (h *Http) SupportWithDialer() bool {
|
func (h *Http) SupportWithDialer() C.NetWork {
|
||||||
return true
|
return C.TCP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
|
func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
@ -28,6 +29,7 @@ import (
|
|||||||
"github.com/Dreamacro/clash/transport/hysteria/obfs"
|
"github.com/Dreamacro/clash/transport/hysteria/obfs"
|
||||||
"github.com/Dreamacro/clash/transport/hysteria/pmtud_fix"
|
"github.com/Dreamacro/clash/transport/hysteria/pmtud_fix"
|
||||||
"github.com/Dreamacro/clash/transport/hysteria/transport"
|
"github.com/Dreamacro/clash/transport/hysteria/transport"
|
||||||
|
"github.com/Dreamacro/clash/transport/hysteria/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -46,21 +48,12 @@ var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`)
|
|||||||
type Hysteria struct {
|
type Hysteria struct {
|
||||||
*Base
|
*Base
|
||||||
|
|
||||||
|
option *HysteriaOption
|
||||||
client *core.Client
|
client *core.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||||
hdc := hyDialerWithContext{
|
tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), h.genHdc(ctx, opts...))
|
||||||
ctx: context.Background(),
|
|
||||||
hyDialer: func(network string) (net.PacketConn, error) {
|
|
||||||
return dialer.ListenPacket(ctx, network, "", h.Base.DialOptions(opts...)...)
|
|
||||||
},
|
|
||||||
remoteAddr: func(addr string) (net.Addr, error) {
|
|
||||||
return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), &hdc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -69,20 +62,32 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||||
hdc := hyDialerWithContext{
|
udpConn, err := h.client.DialUDP(h.genHdc(ctx, opts...))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newPacketConn(&hyPacketConn{udpConn}, h), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.PacketDialer {
|
||||||
|
return &hyDialerWithContext{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
hyDialer: func(network string) (net.PacketConn, error) {
|
hyDialer: func(network string) (net.PacketConn, error) {
|
||||||
return dialer.ListenPacket(ctx, network, "", h.Base.DialOptions(opts...)...)
|
var err error
|
||||||
|
var cDialer C.Dialer = dialer.NewDialer(h.Base.DialOptions(opts...)...)
|
||||||
|
if len(h.option.DialerProxy) > 0 {
|
||||||
|
cDialer, err = proxydialer.NewByName(h.option.DialerProxy, cDialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rAddrPort, _ := netip.ParseAddrPort(h.Addr())
|
||||||
|
return cDialer.ListenPacket(ctx, network, "", rAddrPort)
|
||||||
},
|
},
|
||||||
remoteAddr: func(addr string) (net.Addr, error) {
|
remoteAddr: func(addr string) (net.Addr, error) {
|
||||||
return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer)
|
return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
udpConn, err := h.client.DialUDP(&hdc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return newPacketConn(&hyPacketConn{udpConn}, h), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HysteriaOption struct {
|
type HysteriaOption struct {
|
||||||
@ -258,6 +263,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
|
|||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
},
|
},
|
||||||
|
option: &option,
|
||||||
client: client,
|
client: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
N "github.com/Dreamacro/clash/common/net"
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
"github.com/Dreamacro/clash/common/structure"
|
"github.com/Dreamacro/clash/common/structure"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/transport/restls"
|
"github.com/Dreamacro/clash/transport/restls"
|
||||||
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
|
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
|
||||||
@ -145,6 +147,12 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, op
|
|||||||
|
|
||||||
// DialContextWithDialer implements C.ProxyAdapter
|
// DialContextWithDialer implements C.ProxyAdapter
|
||||||
func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
|
if len(ss.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", ss.addr)
|
c, err := dialer.DialContext(ctx, "tcp", ss.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
|
||||||
@ -166,17 +174,18 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta
|
|||||||
|
|
||||||
// ListenPacketWithDialer implements C.ProxyAdapter
|
// ListenPacketWithDialer implements C.ProxyAdapter
|
||||||
func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if len(ss.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
if ss.option.UDPOverTCP {
|
if ss.option.UDPOverTCP {
|
||||||
tcpConn, err := ss.DialContextWithDialer(ctx, dialer, metadata)
|
tcpConn, err := ss.DialContextWithDialer(ctx, dialer, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
destination := M.ParseSocksaddr(metadata.RemoteAddress())
|
return ss.ListenPacketOnStreamConn(ctx, tcpConn, metadata)
|
||||||
if ss.option.UDPOverTCPVersion == 1 {
|
|
||||||
return newPacketConn(uot.NewConn(tcpConn, uot.Request{Destination: destination}), ss), nil
|
|
||||||
} else {
|
|
||||||
return newPacketConn(uot.NewLazyConn(tcpConn, uot.Request{Destination: destination}), ss), nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer)
|
addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -187,26 +196,35 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pc = ss.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: pc, Addr: addr})
|
pc = ss.method.DialPacketConn(bufio.NewBindPacketConn(pc, addr))
|
||||||
return newPacketConn(pc, ss), nil
|
return newPacketConn(pc, ss), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (ss *ShadowSocks) SupportWithDialer() bool {
|
func (ss *ShadowSocks) SupportWithDialer() C.NetWork {
|
||||||
return true
|
return C.ALLNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
||||||
func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (ss *ShadowSocks) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
if ss.option.UDPOverTCP {
|
if ss.option.UDPOverTCP {
|
||||||
destination := M.ParseSocksaddr(metadata.RemoteAddress())
|
// ss uot use stream-oriented udp with a special address, so we need a net.UDPAddr
|
||||||
|
if !metadata.Resolved() {
|
||||||
|
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("can't resolve ip")
|
||||||
|
}
|
||||||
|
metadata.DstIP = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
destination := M.SocksaddrFromNet(metadata.UDPAddr())
|
||||||
if ss.option.UDPOverTCPVersion == uot.LegacyVersion {
|
if ss.option.UDPOverTCPVersion == uot.LegacyVersion {
|
||||||
return newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), ss), nil
|
return newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), ss), nil
|
||||||
} else {
|
} else {
|
||||||
return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), ss), nil
|
return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), ss), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, errors.New("no support")
|
return nil, C.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportUOT implements C.ProxyAdapter
|
// SupportUOT implements C.ProxyAdapter
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
||||||
"github.com/Dreamacro/clash/transport/shadowsocks/shadowaead"
|
"github.com/Dreamacro/clash/transport/shadowsocks/shadowaead"
|
||||||
@ -17,6 +18,7 @@ import (
|
|||||||
|
|
||||||
type ShadowSocksR struct {
|
type ShadowSocksR struct {
|
||||||
*Base
|
*Base
|
||||||
|
option *ShadowSocksROption
|
||||||
cipher core.Cipher
|
cipher core.Cipher
|
||||||
obfs obfs.Obfs
|
obfs obfs.Obfs
|
||||||
protocol protocol.Protocol
|
protocol protocol.Protocol
|
||||||
@ -65,6 +67,12 @@ func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata,
|
|||||||
|
|
||||||
// DialContextWithDialer implements C.ProxyAdapter
|
// DialContextWithDialer implements C.ProxyAdapter
|
||||||
func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
|
if len(ssr.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(ssr.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", ssr.addr)
|
c, err := dialer.DialContext(ctx, "tcp", ssr.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err)
|
||||||
@ -86,6 +94,12 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me
|
|||||||
|
|
||||||
// ListenPacketWithDialer implements C.ProxyAdapter
|
// ListenPacketWithDialer implements C.ProxyAdapter
|
||||||
func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if len(ssr.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(ssr.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer)
|
addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -102,8 +116,8 @@ func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Di
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (ssr *ShadowSocksR) SupportWithDialer() bool {
|
func (ssr *ShadowSocksR) SupportWithDialer() C.NetWork {
|
||||||
return true
|
return C.ALLNet
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
||||||
@ -168,6 +182,7 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
|||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
},
|
},
|
||||||
|
option: &option,
|
||||||
cipher: coreCiph,
|
cipher: coreCiph,
|
||||||
obfs: obfs,
|
obfs: obfs,
|
||||||
protocol: protocol,
|
protocol: protocol,
|
||||||
|
138
adapter/outbound/singmux.go
Normal file
138
adapter/outbound/singmux.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package outbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
CN "github.com/Dreamacro/clash/common/net"
|
||||||
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
|
||||||
|
mux "github.com/sagernet/sing-mux"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SingMux struct {
|
||||||
|
C.ProxyAdapter
|
||||||
|
base ProxyBase
|
||||||
|
client *mux.Client
|
||||||
|
dialer *muxSingDialer
|
||||||
|
onlyTcp bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type SingMuxOption struct {
|
||||||
|
Enabled bool `proxy:"enabled,omitempty"`
|
||||||
|
Protocol string `proxy:"protocol,omitempty"`
|
||||||
|
MaxConnections int `proxy:"max-connections,omitempty"`
|
||||||
|
MinStreams int `proxy:"min-streams,omitempty"`
|
||||||
|
MaxStreams int `proxy:"max-streams,omitempty"`
|
||||||
|
Padding bool `proxy:"padding,omitempty"`
|
||||||
|
Statistic bool `proxy:"statistic,omitempty"`
|
||||||
|
OnlyTcp bool `proxy:"only-tcp,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyBase interface {
|
||||||
|
DialOptions(opts ...dialer.Option) []dialer.Option
|
||||||
|
}
|
||||||
|
|
||||||
|
type muxSingDialer struct {
|
||||||
|
dialer dialer.Dialer
|
||||||
|
proxy C.ProxyAdapter
|
||||||
|
statistic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ N.Dialer = (*muxSingDialer)(nil)
|
||||||
|
|
||||||
|
func (d *muxSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, d.statistic)
|
||||||
|
return cDialer.DialContext(ctx, network, destination.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *muxSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, d.statistic)
|
||||||
|
return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SingMux) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
|
||||||
|
options := s.base.DialOptions(opts...)
|
||||||
|
s.dialer.dialer = dialer.NewDialer(options...)
|
||||||
|
c, err := s.client.DialContext(ctx, "tcp", M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewConn(CN.NewRefConn(c, s), s.ProxyAdapter), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
|
||||||
|
if s.onlyTcp {
|
||||||
|
return s.ProxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
||||||
|
}
|
||||||
|
options := s.base.DialOptions(opts...)
|
||||||
|
s.dialer.dialer = dialer.NewDialer(options...)
|
||||||
|
|
||||||
|
// sing-mux use stream-oriented udp with a special address, so we need a net.UDPAddr
|
||||||
|
if !metadata.Resolved() {
|
||||||
|
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("can't resolve ip")
|
||||||
|
}
|
||||||
|
metadata.DstIP = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
pc, err := s.client.ListenPacket(ctx, M.SocksaddrFromNet(metadata.UDPAddr()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if pc == nil {
|
||||||
|
return nil, E.New("packetConn is nil")
|
||||||
|
}
|
||||||
|
return newPacketConn(CN.NewRefPacketConn(pc, s), s.ProxyAdapter), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SingMux) SupportUDP() bool {
|
||||||
|
if s.onlyTcp {
|
||||||
|
return s.ProxyAdapter.SupportUOT()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SingMux) SupportUOT() bool {
|
||||||
|
if s.onlyTcp {
|
||||||
|
return s.ProxyAdapter.SupportUOT()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeSingMux(s *SingMux) {
|
||||||
|
_ = s.client.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.ProxyAdapter, error) {
|
||||||
|
singDialer := &muxSingDialer{dialer: dialer.NewDialer(), proxy: proxy, statistic: option.Statistic}
|
||||||
|
client, err := mux.NewClient(mux.Options{
|
||||||
|
Dialer: singDialer,
|
||||||
|
Protocol: option.Protocol,
|
||||||
|
MaxConnections: option.MaxConnections,
|
||||||
|
MinStreams: option.MinStreams,
|
||||||
|
MaxStreams: option.MaxStreams,
|
||||||
|
Padding: option.Padding,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
outbound := &SingMux{
|
||||||
|
ProxyAdapter: proxy,
|
||||||
|
base: base,
|
||||||
|
client: client,
|
||||||
|
dialer: singDialer,
|
||||||
|
onlyTcp: option.OnlyTcp,
|
||||||
|
}
|
||||||
|
runtime.SetFinalizer(outbound, closeSingMux)
|
||||||
|
return outbound, nil
|
||||||
|
}
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Dreamacro/clash/common/structure"
|
"github.com/Dreamacro/clash/common/structure"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
|
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
|
||||||
"github.com/Dreamacro/clash/transport/snell"
|
"github.com/Dreamacro/clash/transport/snell"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
|
|
||||||
type Snell struct {
|
type Snell struct {
|
||||||
*Base
|
*Base
|
||||||
|
option *SnellOption
|
||||||
psk []byte
|
psk []byte
|
||||||
pool *snell.Pool
|
pool *snell.Pool
|
||||||
obfsOption *simpleObfsOption
|
obfsOption *simpleObfsOption
|
||||||
@ -83,6 +85,12 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
|
|||||||
|
|
||||||
// DialContextWithDialer implements C.ProxyAdapter
|
// DialContextWithDialer implements C.ProxyAdapter
|
||||||
func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
|
if len(s.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(s.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", s.addr)
|
c, err := dialer.DialContext(ctx, "tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", s.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", s.addr, err)
|
||||||
@ -104,6 +112,13 @@ func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
|
|
||||||
// ListenPacketWithDialer implements C.ProxyAdapter
|
// ListenPacketWithDialer implements C.ProxyAdapter
|
||||||
func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.PacketConn, error) {
|
func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.PacketConn, error) {
|
||||||
|
var err error
|
||||||
|
if len(s.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(s.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", s.addr)
|
c, err := dialer.DialContext(ctx, "tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -121,8 +136,8 @@ func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (s *Snell) SupportWithDialer() bool {
|
func (s *Snell) SupportWithDialer() C.NetWork {
|
||||||
return true
|
return C.ALLNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportUOT implements C.ProxyAdapter
|
// SupportUOT implements C.ProxyAdapter
|
||||||
@ -172,6 +187,7 @@ func NewSnell(option SnellOption) (*Snell, error) {
|
|||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
},
|
},
|
||||||
|
option: &option,
|
||||||
psk: psk,
|
psk: psk,
|
||||||
obfsOption: obfsOption,
|
obfsOption: obfsOption,
|
||||||
version: option.Version,
|
version: option.Version,
|
||||||
@ -179,7 +195,15 @@ func NewSnell(option SnellOption) (*Snell, error) {
|
|||||||
|
|
||||||
if option.Version == snell.Version2 {
|
if option.Version == snell.Version2 {
|
||||||
s.pool = snell.NewPool(func(ctx context.Context) (*snell.Snell, error) {
|
s.pool = snell.NewPool(func(ctx context.Context) (*snell.Snell, error) {
|
||||||
c, err := dialer.DialContext(ctx, "tcp", addr, s.Base.DialOptions()...)
|
var err error
|
||||||
|
var cDialer C.Dialer = dialer.NewDialer(s.Base.DialOptions()...)
|
||||||
|
if len(s.option.DialerProxy) > 0 {
|
||||||
|
cDialer, err = proxydialer.NewByName(s.option.DialerProxy, cDialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c, err := cDialer.DialContext(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/transport/socks5"
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
@ -17,6 +18,7 @@ import (
|
|||||||
|
|
||||||
type Socks5 struct {
|
type Socks5 struct {
|
||||||
*Base
|
*Base
|
||||||
|
option *Socks5Option
|
||||||
user string
|
user string
|
||||||
pass string
|
pass string
|
||||||
tls bool
|
tls bool
|
||||||
@ -70,6 +72,12 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
|||||||
|
|
||||||
// DialContextWithDialer implements C.ProxyAdapter
|
// DialContextWithDialer implements C.ProxyAdapter
|
||||||
func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
|
if len(ss.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", ss.addr)
|
c, err := dialer.DialContext(ctx, "tcp", ss.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
|
||||||
@ -89,13 +97,20 @@ func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, me
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (ss *Socks5) SupportWithDialer() bool {
|
func (ss *Socks5) SupportWithDialer() C.NetWork {
|
||||||
return true
|
return C.TCP
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
|
func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
|
||||||
c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...)
|
var cDialer C.Dialer = dialer.NewDialer(ss.Base.DialOptions(opts...)...)
|
||||||
|
if len(ss.option.DialerProxy) > 0 {
|
||||||
|
cDialer, err = proxydialer.NewByName(ss.option.DialerProxy, cDialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c, err := cDialer.DialContext(ctx, "tcp", ss.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("%s connect error: %w", ss.addr, err)
|
err = fmt.Errorf("%s connect error: %w", ss.addr, err)
|
||||||
return
|
return
|
||||||
@ -187,6 +202,7 @@ func NewSocks5(option Socks5Option) (*Socks5, error) {
|
|||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
},
|
},
|
||||||
|
option: &option,
|
||||||
user: option.UserName,
|
user: option.UserName,
|
||||||
pass: option.Password,
|
pass: option.Password,
|
||||||
tls: option.TLS,
|
tls: option.TLS,
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
N "github.com/Dreamacro/clash/common/net"
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/transport/gun"
|
"github.com/Dreamacro/clash/transport/gun"
|
||||||
@ -105,7 +105,7 @@ func (t *Trojan) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error)
|
|||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata))
|
err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata))
|
||||||
return N.NewExtendedConn(c), err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
@ -135,6 +135,12 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
|
|||||||
|
|
||||||
// DialContextWithDialer implements C.ProxyAdapter
|
// DialContextWithDialer implements C.ProxyAdapter
|
||||||
func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
|
if len(t.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", t.addr)
|
c, err := dialer.DialContext(ctx, "tcp", t.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
||||||
@ -179,6 +185,12 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
|
|||||||
|
|
||||||
// ListenPacketWithDialer implements C.ProxyAdapter
|
// ListenPacketWithDialer implements C.ProxyAdapter
|
||||||
func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if len(t.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", t.addr)
|
c, err := dialer.DialContext(ctx, "tcp", t.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
||||||
@ -202,8 +214,8 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (t *Trojan) SupportWithDialer() bool {
|
func (t *Trojan) SupportWithDialer() C.NetWork {
|
||||||
return true
|
return C.ALLNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
||||||
@ -271,7 +283,15 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
|||||||
|
|
||||||
if option.Network == "grpc" {
|
if option.Network == "grpc" {
|
||||||
dialFn := func(network, addr string) (net.Conn, error) {
|
dialFn := func(network, addr string) (net.Conn, error) {
|
||||||
c, err := dialer.DialContext(context.Background(), "tcp", t.addr, t.Base.DialOptions()...)
|
var err error
|
||||||
|
var cDialer C.Dialer = dialer.NewDialer(t.Base.DialOptions()...)
|
||||||
|
if len(t.option.DialerProxy) > 0 {
|
||||||
|
cDialer, err = proxydialer.NewByName(t.option.DialerProxy, cDialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c, err := cDialer.DialContext(context.Background(), "tcp", t.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error())
|
return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error())
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/metacubex/quic-go"
|
"github.com/metacubex/quic-go"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/transport/tuic"
|
"github.com/Dreamacro/clash/transport/tuic"
|
||||||
@ -23,6 +24,7 @@ import (
|
|||||||
|
|
||||||
type Tuic struct {
|
type Tuic struct {
|
||||||
*Base
|
*Base
|
||||||
|
option *TuicOption
|
||||||
client *tuic.PoolClient
|
client *tuic.PoolClient
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,8 +86,8 @@ func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, meta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (t *Tuic) SupportWithDialer() bool {
|
func (t *Tuic) SupportWithDialer() C.NetWork {
|
||||||
return true
|
return C.ALLNet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) {
|
func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) {
|
||||||
@ -93,6 +95,12 @@ func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (pc net.PacketConn, addr net.Addr, err error) {
|
func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (pc net.PacketConn, addr net.Addr, err error) {
|
||||||
|
if len(t.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer)
|
udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -230,6 +238,7 @@ func NewTuic(option TuicOption) (*Tuic, error) {
|
|||||||
rmark: option.RoutingMark,
|
rmark: option.RoutingMark,
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
},
|
},
|
||||||
|
option: &option,
|
||||||
}
|
}
|
||||||
|
|
||||||
clientMaxOpenStreams := int64(option.MaxOpenStreams)
|
clientMaxOpenStreams := int64(option.MaxOpenStreams)
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Dreamacro/clash/common/convert"
|
"github.com/Dreamacro/clash/common/convert"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -168,7 +169,35 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP))
|
return v.streamConn(c, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Vless) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) {
|
||||||
|
if metadata.NetWork == C.UDP {
|
||||||
|
if v.option.PacketAddr {
|
||||||
|
metadata = &C.Metadata{
|
||||||
|
NetWork: C.UDP,
|
||||||
|
Host: packetaddr.SeqPacketMagicAddress,
|
||||||
|
DstPort: "443",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
metadata = &C.Metadata{ // a clear metadata only contains ip
|
||||||
|
NetWork: C.UDP,
|
||||||
|
DstIP: metadata.DstIP,
|
||||||
|
DstPort: metadata.DstPort,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP))
|
||||||
|
if v.option.PacketAddr {
|
||||||
|
conn = packetaddr.NewBindConn(conn)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
conn, err = v.client.StreamConn(c, parseVlessAddr(metadata, false))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
conn = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) {
|
func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) {
|
||||||
@ -238,6 +267,12 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
|
|||||||
|
|
||||||
// DialContextWithDialer implements C.ProxyAdapter
|
// DialContextWithDialer implements C.ProxyAdapter
|
||||||
func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
|
if len(v.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
||||||
@ -264,7 +299,6 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
}
|
}
|
||||||
metadata.DstIP = ip
|
metadata.DstIP = ip
|
||||||
}
|
}
|
||||||
|
|
||||||
var c net.Conn
|
var c net.Conn
|
||||||
// gun transport
|
// gun transport
|
||||||
if v.transport != nil && len(opts) == 0 {
|
if v.transport != nil && len(opts) == 0 {
|
||||||
@ -276,27 +310,25 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
safeConnClose(c, err)
|
safeConnClose(c, err)
|
||||||
}(c)
|
}(c)
|
||||||
|
|
||||||
if v.option.PacketAddr {
|
c, err = v.streamConn(c, metadata)
|
||||||
packetAddrMetadata := *metadata // make a copy
|
|
||||||
packetAddrMetadata.Host = packetaddr.SeqPacketMagicAddress
|
|
||||||
packetAddrMetadata.DstPort = "443"
|
|
||||||
|
|
||||||
c, err = v.client.StreamConn(c, parseVlessAddr(&packetAddrMetadata, false))
|
|
||||||
} else {
|
|
||||||
c, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("new vless client error: %v", err)
|
return nil, fmt.Errorf("new vless client error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.ListenPacketOnStreamConn(c, metadata)
|
return v.ListenPacketOnStreamConn(ctx, c, metadata)
|
||||||
}
|
}
|
||||||
return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata)
|
return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketWithDialer implements C.ProxyAdapter
|
// ListenPacketWithDialer implements C.ProxyAdapter
|
||||||
func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if len(v.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// vless use stream-oriented udp with a special address, so we need a net.UDPAddr
|
// vless use stream-oriented udp with a special address, so we need a net.UDPAddr
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() {
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
||||||
@ -305,6 +337,7 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
|
|||||||
}
|
}
|
||||||
metadata.DstIP = ip
|
metadata.DstIP = ip
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
||||||
@ -314,39 +347,39 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
|
|||||||
safeConnClose(c, err)
|
safeConnClose(c, err)
|
||||||
}(c)
|
}(c)
|
||||||
|
|
||||||
if v.option.PacketAddr {
|
c, err = v.StreamConn(c, metadata)
|
||||||
packetAddrMetadata := *metadata // make a copy
|
|
||||||
packetAddrMetadata.Host = packetaddr.SeqPacketMagicAddress
|
|
||||||
packetAddrMetadata.DstPort = "443"
|
|
||||||
|
|
||||||
c, err = v.StreamConn(c, &packetAddrMetadata)
|
|
||||||
} else {
|
|
||||||
c, err = v.StreamConn(c, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("new vless client error: %v", err)
|
return nil, fmt.Errorf("new vless client error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.ListenPacketOnStreamConn(c, metadata)
|
return v.ListenPacketOnStreamConn(ctx, c, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (v *Vless) SupportWithDialer() bool {
|
func (v *Vless) SupportWithDialer() C.NetWork {
|
||||||
return true
|
return C.ALLNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
||||||
func (v *Vless) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
// vless use stream-oriented udp with a special address, so we need a net.UDPAddr
|
||||||
|
if !metadata.Resolved() {
|
||||||
|
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("can't resolve ip")
|
||||||
|
}
|
||||||
|
metadata.DstIP = ip
|
||||||
|
}
|
||||||
|
|
||||||
if v.option.XUDP {
|
if v.option.XUDP {
|
||||||
return newPacketConn(&threadSafePacketConn{
|
return newPacketConn(&threadSafePacketConn{
|
||||||
PacketConn: vmessSing.NewXUDPConn(c, M.ParseSocksaddr(metadata.RemoteAddress())),
|
PacketConn: vmessSing.NewXUDPConn(c, M.SocksaddrFromNet(metadata.UDPAddr())),
|
||||||
}, v), nil
|
}, v), nil
|
||||||
} else if v.option.PacketAddr {
|
} else if v.option.PacketAddr {
|
||||||
return newPacketConn(&threadSafePacketConn{
|
return newPacketConn(&threadSafePacketConn{
|
||||||
PacketConn: packetaddr.NewConn(&vlessPacketConn{
|
PacketConn: packetaddr.NewConn(&vlessPacketConn{
|
||||||
Conn: c, rAddr: metadata.UDPAddr(),
|
Conn: c, rAddr: metadata.UDPAddr(),
|
||||||
}, M.ParseSocksaddr(metadata.RemoteAddress())),
|
}, M.SocksaddrFromNet(metadata.UDPAddr())),
|
||||||
}, v), nil
|
}, v), nil
|
||||||
}
|
}
|
||||||
return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
|
return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
|
||||||
@ -504,6 +537,9 @@ func NewVless(option VlessOption) (*Vless, error) {
|
|||||||
option.XUDP = true
|
option.XUDP = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if option.XUDP {
|
||||||
|
option.PacketAddr = false
|
||||||
|
}
|
||||||
|
|
||||||
client, err := vless.NewClient(option.UUID, addons, option.FlowShow)
|
client, err := vless.NewClient(option.UUID, addons, option.FlowShow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -538,7 +574,15 @@ func NewVless(option VlessOption) (*Vless, error) {
|
|||||||
}
|
}
|
||||||
case "grpc":
|
case "grpc":
|
||||||
dialFn := func(network, addr string) (net.Conn, error) {
|
dialFn := func(network, addr string) (net.Conn, error) {
|
||||||
c, err := dialer.DialContext(context.Background(), "tcp", v.addr, v.Base.DialOptions()...)
|
var err error
|
||||||
|
var cDialer C.Dialer = dialer.NewDialer(v.Base.DialOptions()...)
|
||||||
|
if len(v.option.DialerProxy) > 0 {
|
||||||
|
cDialer, err = proxydialer.NewByName(v.option.DialerProxy, cDialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c, err := cDialer.DialContext(context.Background(), "tcp", v.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
N "github.com/Dreamacro/clash/common/net"
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -216,27 +217,42 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return v.streamConn(c, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Vmess) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) {
|
||||||
if metadata.NetWork == C.UDP {
|
if metadata.NetWork == C.UDP {
|
||||||
if v.option.XUDP {
|
if v.option.XUDP {
|
||||||
if N.NeedHandshake(c) {
|
if N.NeedHandshake(c) {
|
||||||
return v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
|
conn = v.client.DialEarlyXUDPPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
|
||||||
} else {
|
} else {
|
||||||
return v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
conn, err = v.client.DialXUDPPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
|
||||||
}
|
}
|
||||||
|
} else if v.option.PacketAddr {
|
||||||
|
if N.NeedHandshake(c) {
|
||||||
|
conn = v.client.DialEarlyPacketConn(c, M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))
|
||||||
|
} else {
|
||||||
|
conn, err = v.client.DialPacketConn(c, M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))
|
||||||
|
}
|
||||||
|
conn = packetaddr.NewBindConn(conn)
|
||||||
} else {
|
} else {
|
||||||
if N.NeedHandshake(c) {
|
if N.NeedHandshake(c) {
|
||||||
return v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
|
conn = v.client.DialEarlyPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
|
||||||
} else {
|
} else {
|
||||||
return v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
conn, err = v.client.DialPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if N.NeedHandshake(c) {
|
if N.NeedHandshake(c) {
|
||||||
return v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
|
conn = v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||||
} else {
|
} else {
|
||||||
return v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
conn, err = v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
conn = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
@ -263,6 +279,12 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
|
|||||||
|
|
||||||
// DialContextWithDialer implements C.ProxyAdapter
|
// DialContextWithDialer implements C.ProxyAdapter
|
||||||
func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
|
||||||
|
if len(v.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
||||||
@ -286,14 +308,6 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
}
|
}
|
||||||
metadata.DstIP = ip
|
metadata.DstIP = ip
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.option.PacketAddr {
|
|
||||||
_metadata := *metadata // make a copy
|
|
||||||
metadata = &_metadata
|
|
||||||
metadata.Host = packetaddr.SeqPacketMagicAddress
|
|
||||||
metadata.DstPort = "443"
|
|
||||||
}
|
|
||||||
|
|
||||||
var c net.Conn
|
var c net.Conn
|
||||||
// gun transport
|
// gun transport
|
||||||
if v.transport != nil && len(opts) == 0 {
|
if v.transport != nil && len(opts) == 0 {
|
||||||
@ -305,30 +319,24 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
safeConnClose(c, err)
|
safeConnClose(c, err)
|
||||||
}(c)
|
}(c)
|
||||||
|
|
||||||
if v.option.XUDP {
|
c, err = v.streamConn(c, metadata)
|
||||||
if N.NeedHandshake(c) {
|
|
||||||
c = v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
|
||||||
} else {
|
|
||||||
c, err = v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if N.NeedHandshake(c) {
|
|
||||||
c = v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
|
||||||
} else {
|
|
||||||
c, err = v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("new vmess client error: %v", err)
|
return nil, fmt.Errorf("new vmess client error: %v", err)
|
||||||
}
|
}
|
||||||
return v.ListenPacketOnStreamConn(c, metadata)
|
return v.ListenPacketOnStreamConn(ctx, c, metadata)
|
||||||
}
|
}
|
||||||
return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata)
|
return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketWithDialer implements C.ProxyAdapter
|
// ListenPacketWithDialer implements C.ProxyAdapter
|
||||||
func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if len(v.option.DialerProxy) > 0 {
|
||||||
|
dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
|
// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() {
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
||||||
@ -351,19 +359,26 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("new vmess client error: %v", err)
|
return nil, fmt.Errorf("new vmess client error: %v", err)
|
||||||
}
|
}
|
||||||
return v.ListenPacketOnStreamConn(c, metadata)
|
return v.ListenPacketOnStreamConn(ctx, c, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportWithDialer implements C.ProxyAdapter
|
// SupportWithDialer implements C.ProxyAdapter
|
||||||
func (v *Vmess) SupportWithDialer() bool {
|
func (v *Vmess) SupportWithDialer() C.NetWork {
|
||||||
return true
|
return C.ALLNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
||||||
func (v *Vmess) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
if v.option.PacketAddr {
|
// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
|
||||||
return newPacketConn(&threadSafePacketConn{PacketConn: packetaddr.NewBindConn(c)}, v), nil
|
if !metadata.Resolved() {
|
||||||
} else if pc, ok := c.(net.PacketConn); ok {
|
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("can't resolve ip")
|
||||||
|
}
|
||||||
|
metadata.DstIP = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
if pc, ok := c.(net.PacketConn); ok {
|
||||||
return newPacketConn(&threadSafePacketConn{PacketConn: pc}, v), nil
|
return newPacketConn(&threadSafePacketConn{PacketConn: pc}, v), nil
|
||||||
}
|
}
|
||||||
return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
|
return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
|
||||||
@ -428,7 +443,15 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
|||||||
}
|
}
|
||||||
case "grpc":
|
case "grpc":
|
||||||
dialFn := func(network, addr string) (net.Conn, error) {
|
dialFn := func(network, addr string) (net.Conn, error) {
|
||||||
c, err := dialer.DialContext(context.Background(), "tcp", v.addr, v.Base.DialOptions()...)
|
var err error
|
||||||
|
var cDialer C.Dialer = dialer.NewDialer(v.Base.DialOptions()...)
|
||||||
|
if len(v.option.DialerProxy) > 0 {
|
||||||
|
cDialer, err = proxydialer.NewByName(v.option.DialerProxy, cDialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c, err := cDialer.DialContext(context.Background(), "tcp", v.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
|
||||||
}
|
}
|
||||||
@ -486,9 +509,9 @@ type vmessPacketConn struct {
|
|||||||
// WriteTo implments C.PacketConn.WriteTo
|
// WriteTo implments C.PacketConn.WriteTo
|
||||||
// Since VMess doesn't support full cone NAT by design, we verify if addr matches uc.rAddr, and drop the packet if not.
|
// Since VMess doesn't support full cone NAT by design, we verify if addr matches uc.rAddr, and drop the packet if not.
|
||||||
func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||||
allowedAddr := uc.rAddr.(*net.UDPAddr)
|
allowedAddr := uc.rAddr
|
||||||
destAddr := addr.(*net.UDPAddr)
|
destAddr := addr
|
||||||
if !(allowedAddr.IP.Equal(destAddr.IP) && allowedAddr.Port == destAddr.Port) {
|
if allowedAddr.String() != destAddr.String() {
|
||||||
return 0, ErrUDPRemoteAddrMismatch
|
return 0, ErrUDPRemoteAddrMismatch
|
||||||
}
|
}
|
||||||
uc.access.Lock()
|
uc.access.Lock()
|
||||||
|
@ -15,8 +15,10 @@ import (
|
|||||||
|
|
||||||
CN "github.com/Dreamacro/clash/common/net"
|
CN "github.com/Dreamacro/clash/common/net"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/dns"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
wireguard "github.com/metacubex/sing-wireguard"
|
wireguard "github.com/metacubex/sing-wireguard"
|
||||||
@ -37,75 +39,97 @@ type WireGuard struct {
|
|||||||
dialer *wgSingDialer
|
dialer *wgSingDialer
|
||||||
startOnce sync.Once
|
startOnce sync.Once
|
||||||
startErr error
|
startErr error
|
||||||
|
resolver *dns.Resolver
|
||||||
|
refP *refProxyAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
type WireGuardOption struct {
|
type WireGuardOption struct {
|
||||||
BasicOption
|
BasicOption
|
||||||
Name string `proxy:"name"`
|
WireGuardPeerOption
|
||||||
Server string `proxy:"server"`
|
Name string `proxy:"name"`
|
||||||
Port int `proxy:"port"`
|
PrivateKey string `proxy:"private-key"`
|
||||||
Ip string `proxy:"ip,omitempty"`
|
Workers int `proxy:"workers,omitempty"`
|
||||||
Ipv6 string `proxy:"ipv6,omitempty"`
|
MTU int `proxy:"mtu,omitempty"`
|
||||||
PrivateKey string `proxy:"private-key"`
|
UDP bool `proxy:"udp,omitempty"`
|
||||||
PublicKey string `proxy:"public-key"`
|
PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"`
|
||||||
PreSharedKey string `proxy:"pre-shared-key,omitempty"`
|
|
||||||
Reserved []uint8 `proxy:"reserved,omitempty"`
|
Peers []WireGuardPeerOption `proxy:"peers,omitempty"`
|
||||||
Workers int `proxy:"workers,omitempty"`
|
|
||||||
MTU int `proxy:"mtu,omitempty"`
|
RemoteDnsResolve bool `proxy:"remote-dns-resolve,omitempty"`
|
||||||
UDP bool `proxy:"udp,omitempty"`
|
Dns []string `proxy:"dns,omitempty"`
|
||||||
PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"`
|
}
|
||||||
|
|
||||||
|
type WireGuardPeerOption struct {
|
||||||
|
Server string `proxy:"server"`
|
||||||
|
Port int `proxy:"port"`
|
||||||
|
Ip string `proxy:"ip,omitempty"`
|
||||||
|
Ipv6 string `proxy:"ipv6,omitempty"`
|
||||||
|
PublicKey string `proxy:"public-key,omitempty"`
|
||||||
|
PreSharedKey string `proxy:"pre-shared-key,omitempty"`
|
||||||
|
Reserved []uint8 `proxy:"reserved,omitempty"`
|
||||||
|
AllowedIPs []string `proxy:"allowed_ips,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type wgSingDialer struct {
|
type wgSingDialer struct {
|
||||||
dialer dialer.Dialer
|
dialer dialer.Dialer
|
||||||
|
proxyName string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ N.Dialer = &wgSingDialer{}
|
var _ N.Dialer = (*wgSingDialer)(nil)
|
||||||
|
|
||||||
func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
return d.dialer.DialContext(ctx, network, destination.String())
|
var cDialer C.Dialer = d.dialer
|
||||||
|
if len(d.proxyName) > 0 {
|
||||||
|
pd, err := proxydialer.NewByName(d.proxyName, d.dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cDialer = pd
|
||||||
|
}
|
||||||
|
return cDialer.DialContext(ctx, network, destination.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
return d.dialer.ListenPacket(ctx, "udp", "", destination.AddrPort())
|
var cDialer C.Dialer = d.dialer
|
||||||
|
if len(d.proxyName) > 0 {
|
||||||
|
pd, err := proxydialer.NewByName(d.proxyName, d.dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cDialer = pd
|
||||||
|
}
|
||||||
|
return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort())
|
||||||
|
}
|
||||||
|
|
||||||
|
type wgSingErrorHandler struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ E.Handler = (*wgSingErrorHandler)(nil)
|
||||||
|
|
||||||
|
func (w wgSingErrorHandler) NewError(ctx context.Context, err error) {
|
||||||
|
if E.IsClosedOrCanceled(err) {
|
||||||
|
log.SingLogger.Debug(fmt.Sprintf("[WG](%s) connection closed: %s", w.name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", w.name, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
type wgNetDialer struct {
|
type wgNetDialer struct {
|
||||||
tunDevice wireguard.Device
|
tunDevice wireguard.Device
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ dialer.NetDialer = &wgNetDialer{}
|
var _ dialer.NetDialer = (*wgNetDialer)(nil)
|
||||||
|
|
||||||
func (d wgNetDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
func (d wgNetDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
return d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address).Unwrap())
|
return d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address).Unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
func (option WireGuardPeerOption) Addr() M.Socksaddr {
|
||||||
outbound := &WireGuard{
|
return M.ParseSocksaddrHostPort(option.Server, uint16(option.Port))
|
||||||
Base: &Base{
|
}
|
||||||
name: option.Name,
|
|
||||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
|
||||||
tp: C.WireGuard,
|
|
||||||
udp: option.UDP,
|
|
||||||
iface: option.Interface,
|
|
||||||
rmark: option.RoutingMark,
|
|
||||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
|
||||||
},
|
|
||||||
dialer: &wgSingDialer{dialer: dialer.NewDialer()},
|
|
||||||
}
|
|
||||||
runtime.SetFinalizer(outbound, closeWireGuard)
|
|
||||||
|
|
||||||
var reserved [3]uint8
|
func (option WireGuardPeerOption) Prefixes() ([]netip.Prefix, error) {
|
||||||
if len(option.Reserved) > 0 {
|
|
||||||
if len(option.Reserved) != 3 {
|
|
||||||
return nil, E.New("invalid reserved value, required 3 bytes, got ", len(option.Reserved))
|
|
||||||
}
|
|
||||||
reserved[0] = uint8(option.Reserved[0])
|
|
||||||
reserved[1] = uint8(option.Reserved[1])
|
|
||||||
reserved[2] = uint8(option.Reserved[2])
|
|
||||||
}
|
|
||||||
peerAddr := M.ParseSocksaddrHostPort(option.Server, uint16(option.Port))
|
|
||||||
outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, peerAddr, reserved)
|
|
||||||
localPrefixes := make([]netip.Prefix, 0, 2)
|
localPrefixes := make([]netip.Prefix, 0, 2)
|
||||||
if len(option.Ip) > 0 {
|
if len(option.Ip) > 0 {
|
||||||
if !strings.Contains(option.Ip, "/") {
|
if !strings.Contains(option.Ip, "/") {
|
||||||
@ -130,7 +154,46 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||||||
if len(localPrefixes) == 0 {
|
if len(localPrefixes) == 0 {
|
||||||
return nil, E.New("missing local address")
|
return nil, E.New("missing local address")
|
||||||
}
|
}
|
||||||
var privateKey, peerPublicKey, preSharedKey string
|
return localPrefixes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
||||||
|
outbound := &WireGuard{
|
||||||
|
Base: &Base{
|
||||||
|
name: option.Name,
|
||||||
|
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||||
|
tp: C.WireGuard,
|
||||||
|
udp: option.UDP,
|
||||||
|
iface: option.Interface,
|
||||||
|
rmark: option.RoutingMark,
|
||||||
|
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||||
|
},
|
||||||
|
dialer: &wgSingDialer{dialer: dialer.NewDialer(), proxyName: option.DialerProxy},
|
||||||
|
}
|
||||||
|
runtime.SetFinalizer(outbound, closeWireGuard)
|
||||||
|
|
||||||
|
var reserved [3]uint8
|
||||||
|
if len(option.Reserved) > 0 {
|
||||||
|
if len(option.Reserved) != 3 {
|
||||||
|
return nil, E.New("invalid reserved value, required 3 bytes, got ", len(option.Reserved))
|
||||||
|
}
|
||||||
|
copy(reserved[:], option.Reserved)
|
||||||
|
}
|
||||||
|
var isConnect bool
|
||||||
|
var connectAddr M.Socksaddr
|
||||||
|
if len(option.Peers) < 2 {
|
||||||
|
isConnect = true
|
||||||
|
if len(option.Peers) == 1 {
|
||||||
|
connectAddr = option.Peers[0].Addr()
|
||||||
|
} else {
|
||||||
|
connectAddr = option.Addr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, connectAddr, reserved)
|
||||||
|
|
||||||
|
var localPrefixes []netip.Prefix
|
||||||
|
|
||||||
|
var privateKey string
|
||||||
{
|
{
|
||||||
bytes, err := base64.StdEncoding.DecodeString(option.PrivateKey)
|
bytes, err := base64.StdEncoding.DecodeString(option.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -138,40 +201,92 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||||||
}
|
}
|
||||||
privateKey = hex.EncodeToString(bytes)
|
privateKey = hex.EncodeToString(bytes)
|
||||||
}
|
}
|
||||||
{
|
|
||||||
bytes, err := base64.StdEncoding.DecodeString(option.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "decode peer public key")
|
|
||||||
}
|
|
||||||
peerPublicKey = hex.EncodeToString(bytes)
|
|
||||||
}
|
|
||||||
if option.PreSharedKey != "" {
|
|
||||||
bytes, err := base64.StdEncoding.DecodeString(option.PreSharedKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "decode pre shared key")
|
|
||||||
}
|
|
||||||
preSharedKey = hex.EncodeToString(bytes)
|
|
||||||
}
|
|
||||||
ipcConf := "private_key=" + privateKey
|
ipcConf := "private_key=" + privateKey
|
||||||
ipcConf += "\npublic_key=" + peerPublicKey
|
if peersLen := len(option.Peers); peersLen > 0 {
|
||||||
ipcConf += "\nendpoint=" + peerAddr.String()
|
localPrefixes = make([]netip.Prefix, 0, peersLen*2)
|
||||||
if preSharedKey != "" {
|
for i, peer := range option.Peers {
|
||||||
ipcConf += "\npreshared_key=" + preSharedKey
|
var peerPublicKey, preSharedKey string
|
||||||
}
|
{
|
||||||
var has4, has6 bool
|
bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey)
|
||||||
for _, address := range localPrefixes {
|
if err != nil {
|
||||||
if address.Addr().Is4() {
|
return nil, E.Cause(err, "decode public key for peer ", i)
|
||||||
has4 = true
|
}
|
||||||
} else {
|
peerPublicKey = hex.EncodeToString(bytes)
|
||||||
has6 = true
|
}
|
||||||
|
if peer.PreSharedKey != "" {
|
||||||
|
bytes, err := base64.StdEncoding.DecodeString(peer.PreSharedKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "decode pre shared key for peer ", i)
|
||||||
|
}
|
||||||
|
preSharedKey = hex.EncodeToString(bytes)
|
||||||
|
}
|
||||||
|
destination := peer.Addr()
|
||||||
|
ipcConf += "\npublic_key=" + peerPublicKey
|
||||||
|
ipcConf += "\nendpoint=" + destination.String()
|
||||||
|
if preSharedKey != "" {
|
||||||
|
ipcConf += "\npreshared_key=" + preSharedKey
|
||||||
|
}
|
||||||
|
if len(peer.AllowedIPs) == 0 {
|
||||||
|
return nil, E.New("missing allowed_ips for peer ", i)
|
||||||
|
}
|
||||||
|
for _, allowedIP := range peer.AllowedIPs {
|
||||||
|
ipcConf += "\nallowed_ip=" + allowedIP
|
||||||
|
}
|
||||||
|
if len(peer.Reserved) > 0 {
|
||||||
|
if len(peer.Reserved) != 3 {
|
||||||
|
return nil, E.New("invalid reserved value for peer ", i, ", required 3 bytes, got ", len(peer.Reserved))
|
||||||
|
}
|
||||||
|
copy(reserved[:], option.Reserved)
|
||||||
|
outbound.bind.SetReservedForEndpoint(destination, reserved)
|
||||||
|
}
|
||||||
|
prefixes, err := peer.Prefixes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
localPrefixes = append(localPrefixes, prefixes...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var peerPublicKey, preSharedKey string
|
||||||
|
{
|
||||||
|
bytes, err := base64.StdEncoding.DecodeString(option.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "decode peer public key")
|
||||||
|
}
|
||||||
|
peerPublicKey = hex.EncodeToString(bytes)
|
||||||
|
}
|
||||||
|
if option.PreSharedKey != "" {
|
||||||
|
bytes, err := base64.StdEncoding.DecodeString(option.PreSharedKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "decode pre shared key")
|
||||||
|
}
|
||||||
|
preSharedKey = hex.EncodeToString(bytes)
|
||||||
|
}
|
||||||
|
ipcConf += "\npublic_key=" + peerPublicKey
|
||||||
|
ipcConf += "\nendpoint=" + connectAddr.String()
|
||||||
|
if preSharedKey != "" {
|
||||||
|
ipcConf += "\npreshared_key=" + preSharedKey
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
localPrefixes, err = option.Prefixes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var has4, has6 bool
|
||||||
|
for _, address := range localPrefixes {
|
||||||
|
if address.Addr().Is4() {
|
||||||
|
has4 = true
|
||||||
|
} else {
|
||||||
|
has6 = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has4 {
|
||||||
|
ipcConf += "\nallowed_ip=0.0.0.0/0"
|
||||||
|
}
|
||||||
|
if has6 {
|
||||||
|
ipcConf += "\nallowed_ip=::/0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if has4 {
|
|
||||||
ipcConf += "\nallowed_ip=0.0.0.0/0"
|
|
||||||
}
|
|
||||||
if has6 {
|
|
||||||
ipcConf += "\nallowed_ip=::/0"
|
|
||||||
}
|
|
||||||
if option.PersistentKeepalive != 0 {
|
if option.PersistentKeepalive != 0 {
|
||||||
ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive)
|
ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive)
|
||||||
}
|
}
|
||||||
@ -179,6 +294,9 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||||||
if mtu == 0 {
|
if mtu == 0 {
|
||||||
mtu = 1408
|
mtu = 1408
|
||||||
}
|
}
|
||||||
|
if len(localPrefixes) == 0 {
|
||||||
|
return nil, E.New("missing local address")
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu))
|
outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -186,20 +304,45 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
|
|||||||
}
|
}
|
||||||
outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{
|
outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{
|
||||||
Verbosef: func(format string, args ...interface{}) {
|
Verbosef: func(format string, args ...interface{}) {
|
||||||
log.SingLogger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
|
log.SingLogger.Debug(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...)))
|
||||||
},
|
},
|
||||||
Errorf: func(format string, args ...interface{}) {
|
Errorf: func(format string, args ...interface{}) {
|
||||||
log.SingLogger.Error(fmt.Sprintf(strings.ToLower(format), args...))
|
log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...)))
|
||||||
},
|
},
|
||||||
}, option.Workers)
|
}, option.Workers)
|
||||||
if debug.Enabled {
|
if debug.Enabled {
|
||||||
log.SingLogger.Trace("created wireguard ipc conf: \n", ipcConf)
|
log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", option.Name, ipcConf))
|
||||||
}
|
}
|
||||||
err = outbound.device.IpcSet(ipcConf)
|
err = outbound.device.IpcSet(ipcConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "setup wireguard")
|
return nil, E.Cause(err, "setup wireguard")
|
||||||
}
|
}
|
||||||
//err = outbound.tunDevice.Start()
|
//err = outbound.tunDevice.Start()
|
||||||
|
|
||||||
|
var has6 bool
|
||||||
|
for _, address := range localPrefixes {
|
||||||
|
if !address.Addr().Unmap().Is4() {
|
||||||
|
has6 = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refP := &refProxyAdapter{}
|
||||||
|
outbound.refP = refP
|
||||||
|
if option.RemoteDnsResolve && len(option.Dns) > 0 {
|
||||||
|
nss, err := dns.ParseNameServer(option.Dns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := range nss {
|
||||||
|
nss[i].ProxyAdapter = refP
|
||||||
|
}
|
||||||
|
outbound.resolver = dns.NewResolver(dns.Config{
|
||||||
|
Main: nss,
|
||||||
|
IPv6: has6,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return outbound, nil
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,8 +363,14 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts
|
|||||||
if w.startErr != nil {
|
if w.startErr != nil {
|
||||||
return nil, w.startErr
|
return nil, w.startErr
|
||||||
}
|
}
|
||||||
if !metadata.Resolved() {
|
if !metadata.Resolved() || w.resolver != nil {
|
||||||
options = append(options, dialer.WithResolver(resolver.DefaultResolver))
|
r := resolver.DefaultResolver
|
||||||
|
if w.resolver != nil {
|
||||||
|
w.refP.SetProxyAdapter(w)
|
||||||
|
defer w.refP.ClearProxyAdapter()
|
||||||
|
r = w.resolver
|
||||||
|
}
|
||||||
|
options = append(options, dialer.WithResolver(r))
|
||||||
options = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: w.tunDevice}))
|
options = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: w.tunDevice}))
|
||||||
conn, err = dialer.NewDialer(options...).DialContext(ctx, "tcp", metadata.RemoteAddress())
|
conn, err = dialer.NewDialer(options...).DialContext(ctx, "tcp", metadata.RemoteAddress())
|
||||||
} else {
|
} else {
|
||||||
@ -250,8 +399,14 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !metadata.Resolved() {
|
if (!metadata.Resolved() || w.resolver != nil) && metadata.Host != "" {
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
r := resolver.DefaultResolver
|
||||||
|
if w.resolver != nil {
|
||||||
|
w.refP.SetProxyAdapter(w)
|
||||||
|
defer w.refP.ClearProxyAdapter()
|
||||||
|
r = w.resolver
|
||||||
|
}
|
||||||
|
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("can't resolve ip")
|
return nil, errors.New("can't resolve ip")
|
||||||
}
|
}
|
||||||
@ -267,3 +422,144 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat
|
|||||||
}
|
}
|
||||||
return newPacketConn(CN.NewRefPacketConn(pc, w), w), nil
|
return newPacketConn(CN.NewRefPacketConn(pc, w), w), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsL3Protocol implements C.ProxyAdapter
|
||||||
|
func (w *WireGuard) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type refProxyAdapter struct {
|
||||||
|
proxyAdapter C.ProxyAdapter
|
||||||
|
count int
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) SetProxyAdapter(proxyAdapter C.ProxyAdapter) {
|
||||||
|
r.mutex.Lock()
|
||||||
|
defer r.mutex.Unlock()
|
||||||
|
r.proxyAdapter = proxyAdapter
|
||||||
|
r.count++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) ClearProxyAdapter() {
|
||||||
|
r.mutex.Lock()
|
||||||
|
defer r.mutex.Unlock()
|
||||||
|
r.count--
|
||||||
|
if r.count == 0 {
|
||||||
|
r.proxyAdapter = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) Name() string {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.Name()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) Type() C.AdapterType {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.Type()
|
||||||
|
}
|
||||||
|
return C.AdapterType(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) Addr() string {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.Addr()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) SupportUDP() bool {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.SupportUDP()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) SupportXUDP() bool {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.SupportXUDP()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) SupportTFO() bool {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.SupportTFO()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) MarshalJSON() ([]byte, error) {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.MarshalJSON()
|
||||||
|
}
|
||||||
|
return nil, C.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.StreamConn(c, metadata)
|
||||||
|
}
|
||||||
|
return nil, C.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.DialContext(ctx, metadata, opts...)
|
||||||
|
}
|
||||||
|
return nil, C.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
||||||
|
}
|
||||||
|
return nil, C.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) SupportUOT() bool {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.SupportUOT()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) SupportWithDialer() C.NetWork {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.SupportWithDialer()
|
||||||
|
}
|
||||||
|
return C.InvalidNet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.Conn, error) {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.DialContextWithDialer(ctx, dialer, metadata)
|
||||||
|
}
|
||||||
|
return nil, C.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.PacketConn, error) {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.ListenPacketWithDialer(ctx, dialer, metadata)
|
||||||
|
}
|
||||||
|
return nil, C.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.IsL3Protocol(metadata)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refProxyAdapter) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
|
||||||
|
if r.proxyAdapter != nil {
|
||||||
|
return r.proxyAdapter.Unwrap(metadata, touch)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ C.ProxyAdapter = (*refProxyAdapter)(nil)
|
||||||
|
@ -37,16 +37,13 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
|||||||
}
|
}
|
||||||
|
|
||||||
if N.NeedHandshake(c) {
|
if N.NeedHandshake(c) {
|
||||||
c = &callback.FirstWriteCallBackConn{
|
c = callback.NewFirstWriteCallBackConn(c, func(err error) {
|
||||||
Conn: c,
|
if err == nil {
|
||||||
Callback: func(err error) {
|
f.onDialSuccess()
|
||||||
if err == nil {
|
} else {
|
||||||
f.onDialSuccess()
|
f.onDialFailed(proxy.Type(), err)
|
||||||
} else {
|
}
|
||||||
f.onDialFailed(proxy.Type(), err)
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, err
|
return c, err
|
||||||
@ -73,6 +70,11 @@ func (f *Fallback) SupportUDP() bool {
|
|||||||
return proxy.SupportUDP()
|
return proxy.SupportUDP()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsL3Protocol implements C.ProxyAdapter
|
||||||
|
func (f *Fallback) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
|
return f.findAliveProxy(false).IsL3Protocol(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalJSON implements C.ProxyAdapter
|
// MarshalJSON implements C.ProxyAdapter
|
||||||
func (f *Fallback) MarshalJSON() ([]byte, error) {
|
func (f *Fallback) MarshalJSON() ([]byte, error) {
|
||||||
all := []string{}
|
all := []string{}
|
||||||
@ -136,6 +138,10 @@ func (f *Fallback) Set(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Fallback) ForceSet(name string) {
|
||||||
|
f.selected = name
|
||||||
|
}
|
||||||
|
|
||||||
func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
|
func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
|
||||||
return &Fallback{
|
return &Fallback{
|
||||||
GroupBase: NewGroupBase(GroupBaseOption{
|
GroupBase: NewGroupBase(GroupBaseOption{
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/constant/provider"
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
types "github.com/Dreamacro/clash/constant/provider"
|
types "github.com/Dreamacro/clash/constant/provider"
|
||||||
@ -15,7 +16,6 @@ import (
|
|||||||
"github.com/Dreamacro/clash/tunnel"
|
"github.com/Dreamacro/clash/tunnel"
|
||||||
|
|
||||||
"github.com/dlclark/regexp2"
|
"github.com/dlclark/regexp2"
|
||||||
"go.uber.org/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type GroupBase struct {
|
type GroupBase struct {
|
||||||
|
@ -95,16 +95,13 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op
|
|||||||
}
|
}
|
||||||
|
|
||||||
if N.NeedHandshake(c) {
|
if N.NeedHandshake(c) {
|
||||||
c = &callback.FirstWriteCallBackConn{
|
c = callback.NewFirstWriteCallBackConn(c, func(err error) {
|
||||||
Conn: c,
|
if err == nil {
|
||||||
Callback: func(err error) {
|
lb.onDialSuccess()
|
||||||
if err == nil {
|
} else {
|
||||||
lb.onDialSuccess()
|
lb.onDialFailed(proxy.Type(), err)
|
||||||
} else {
|
}
|
||||||
lb.onDialFailed(proxy.Type(), err)
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -127,6 +124,11 @@ func (lb *LoadBalance) SupportUDP() bool {
|
|||||||
return !lb.disableUDP
|
return !lb.disableUDP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsL3Protocol implements C.ProxyAdapter
|
||||||
|
func (lb *LoadBalance) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
|
return lb.Unwrap(metadata, false).IsL3Protocol(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
func strategyRoundRobin() strategyFn {
|
func strategyRoundRobin() strategyFn {
|
||||||
idx := 0
|
idx := 0
|
||||||
idxMutex := sync.Mutex{}
|
idxMutex := sync.Mutex{}
|
||||||
|
@ -3,13 +3,9 @@ package outboundgroup
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
N "github.com/Dreamacro/clash/common/net"
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/proxydialer"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/constant/provider"
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
)
|
)
|
||||||
@ -18,36 +14,6 @@ type Relay struct {
|
|||||||
*GroupBase
|
*GroupBase
|
||||||
}
|
}
|
||||||
|
|
||||||
type proxyDialer struct {
|
|
||||||
proxy C.Proxy
|
|
||||||
dialer C.Dialer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
currentMeta, err := addrToMetadata(address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if strings.Contains(network, "udp") { // should not support this operation
|
|
||||||
currentMeta.NetWork = C.UDP
|
|
||||||
pc, err := p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil
|
|
||||||
}
|
|
||||||
return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
|
|
||||||
currentMeta, err := addrToMetadata(rAddrPort.String())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
currentMeta.NetWork = C.UDP
|
|
||||||
return p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||||
proxies, chainProxies := r.proxies(metadata, true)
|
proxies, chainProxies := r.proxies(metadata, true)
|
||||||
@ -61,10 +27,7 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
|
|||||||
var d C.Dialer
|
var d C.Dialer
|
||||||
d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
|
d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
|
||||||
for _, proxy := range proxies[:len(proxies)-1] {
|
for _, proxy := range proxies[:len(proxies)-1] {
|
||||||
d = proxyDialer{
|
d = proxydialer.New(proxy, d, false)
|
||||||
proxy: proxy,
|
|
||||||
dialer: d,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
last := proxies[len(proxies)-1]
|
last := proxies[len(proxies)-1]
|
||||||
conn, err := last.DialContextWithDialer(ctx, d, metadata)
|
conn, err := last.DialContextWithDialer(ctx, d, metadata)
|
||||||
@ -95,10 +58,7 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
|
|||||||
var d C.Dialer
|
var d C.Dialer
|
||||||
d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
|
d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
|
||||||
for _, proxy := range proxies[:len(proxies)-1] {
|
for _, proxy := range proxies[:len(proxies)-1] {
|
||||||
d = proxyDialer{
|
d = proxydialer.New(proxy, d, false)
|
||||||
proxy: proxy,
|
|
||||||
dialer: d,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
last := proxies[len(proxies)-1]
|
last := proxies[len(proxies)-1]
|
||||||
pc, err := last.ListenPacketWithDialer(ctx, d, metadata)
|
pc, err := last.ListenPacketWithDialer(ctx, d, metadata)
|
||||||
@ -129,7 +89,10 @@ func (r *Relay) SupportUDP() bool {
|
|||||||
if proxy.SupportUOT() {
|
if proxy.SupportUOT() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if !proxy.SupportWithDialer() {
|
switch proxy.SupportWithDialer() {
|
||||||
|
case C.ALLNet:
|
||||||
|
case C.UDP:
|
||||||
|
default: // C.TCP and C.InvalidNet
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,11 @@ func (s *Selector) SupportUDP() bool {
|
|||||||
return s.selectedProxy(false).SupportUDP()
|
return s.selectedProxy(false).SupportUDP()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsL3Protocol implements C.ProxyAdapter
|
||||||
|
func (s *Selector) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
|
return s.selectedProxy(false).IsL3Protocol(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalJSON implements C.ProxyAdapter
|
// MarshalJSON implements C.ProxyAdapter
|
||||||
func (s *Selector) MarshalJSON() ([]byte, error) {
|
func (s *Selector) MarshalJSON() ([]byte, error) {
|
||||||
all := []string{}
|
all := []string{}
|
||||||
@ -73,6 +78,10 @@ func (s *Selector) Set(name string) error {
|
|||||||
return errors.New("proxy not exist")
|
return errors.New("proxy not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Selector) ForceSet(name string) {
|
||||||
|
s.selected = name
|
||||||
|
}
|
||||||
|
|
||||||
// Unwrap implements C.ProxyAdapter
|
// Unwrap implements C.ProxyAdapter
|
||||||
func (s *Selector) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
|
func (s *Selector) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
|
||||||
return s.selectedProxy(touch)
|
return s.selectedProxy(touch)
|
||||||
|
@ -3,6 +3,7 @@ package outboundgroup
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
@ -24,6 +25,8 @@ func urlTestWithTolerance(tolerance uint16) urlTestOption {
|
|||||||
|
|
||||||
type URLTest struct {
|
type URLTest struct {
|
||||||
*GroupBase
|
*GroupBase
|
||||||
|
selected string
|
||||||
|
testUrl string
|
||||||
tolerance uint16
|
tolerance uint16
|
||||||
disableUDP bool
|
disableUDP bool
|
||||||
fastNode C.Proxy
|
fastNode C.Proxy
|
||||||
@ -34,6 +37,26 @@ func (u *URLTest) Now() string {
|
|||||||
return u.fast(false).Name()
|
return u.fast(false).Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *URLTest) Set(name string) error {
|
||||||
|
var p C.Proxy
|
||||||
|
for _, proxy := range u.GetProxies(false) {
|
||||||
|
if proxy.Name() == name {
|
||||||
|
p = proxy
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
return errors.New("proxy not exist")
|
||||||
|
}
|
||||||
|
u.selected = name
|
||||||
|
u.fast(false)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *URLTest) ForceSet(name string) {
|
||||||
|
u.selected = name
|
||||||
|
}
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (c C.Conn, err error) {
|
func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (c C.Conn, err error) {
|
||||||
proxy := u.fast(true)
|
proxy := u.fast(true)
|
||||||
@ -45,16 +68,13 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
|||||||
}
|
}
|
||||||
|
|
||||||
if N.NeedHandshake(c) {
|
if N.NeedHandshake(c) {
|
||||||
c = &callback.FirstWriteCallBackConn{
|
c = callback.NewFirstWriteCallBackConn(c, func(err error) {
|
||||||
Conn: c,
|
if err == nil {
|
||||||
Callback: func(err error) {
|
u.onDialSuccess()
|
||||||
if err == nil {
|
} else {
|
||||||
u.onDialSuccess()
|
u.onDialFailed(proxy.Type(), err)
|
||||||
} else {
|
}
|
||||||
u.onDialFailed(proxy.Type(), err)
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, err
|
return c, err
|
||||||
@ -77,16 +97,24 @@ func (u *URLTest) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
|
|||||||
|
|
||||||
func (u *URLTest) fast(touch bool) C.Proxy {
|
func (u *URLTest) fast(touch bool) C.Proxy {
|
||||||
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
|
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
|
||||||
|
var s C.Proxy
|
||||||
proxies := u.GetProxies(touch)
|
proxies := u.GetProxies(touch)
|
||||||
fast := proxies[0]
|
fast := proxies[0]
|
||||||
|
if fast.Name() == u.selected {
|
||||||
|
s = fast
|
||||||
|
}
|
||||||
min := fast.LastDelay()
|
min := fast.LastDelay()
|
||||||
fastNotExist := true
|
fastNotExist := true
|
||||||
|
|
||||||
for _, proxy := range proxies[1:] {
|
for _, proxy := range proxies[1:] {
|
||||||
|
|
||||||
if u.fastNode != nil && proxy.Name() == u.fastNode.Name() {
|
if u.fastNode != nil && proxy.Name() == u.fastNode.Name() {
|
||||||
fastNotExist = false
|
fastNotExist = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if proxy.Name() == u.selected {
|
||||||
|
s = proxy
|
||||||
|
}
|
||||||
if !proxy.Alive() {
|
if !proxy.Alive() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -97,12 +125,15 @@ func (u *URLTest) fast(touch bool) C.Proxy {
|
|||||||
min = delay
|
min = delay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tolerance
|
// tolerance
|
||||||
if u.fastNode == nil || fastNotExist || !u.fastNode.Alive() || u.fastNode.LastDelay() > fast.LastDelay()+u.tolerance {
|
if u.fastNode == nil || fastNotExist || !u.fastNode.Alive() || u.fastNode.LastDelay() > fast.LastDelay()+u.tolerance {
|
||||||
u.fastNode = fast
|
u.fastNode = fast
|
||||||
}
|
}
|
||||||
|
if s != nil {
|
||||||
|
if s.Alive() && s.LastDelay() < fast.LastDelay()+u.tolerance {
|
||||||
|
u.fastNode = s
|
||||||
|
}
|
||||||
|
}
|
||||||
return u.fastNode, nil
|
return u.fastNode, nil
|
||||||
})
|
})
|
||||||
if shared && touch { // a shared fastSingle.Do() may cause providers untouched, so we touch them again
|
if shared && touch { // a shared fastSingle.Do() may cause providers untouched, so we touch them again
|
||||||
@ -117,10 +148,14 @@ func (u *URLTest) SupportUDP() bool {
|
|||||||
if u.disableUDP {
|
if u.disableUDP {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return u.fast(false).SupportUDP()
|
return u.fast(false).SupportUDP()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsL3Protocol implements C.ProxyAdapter
|
||||||
|
func (u *URLTest) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
|
return u.fast(false).IsL3Protocol(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalJSON implements C.ProxyAdapter
|
// MarshalJSON implements C.ProxyAdapter
|
||||||
func (u *URLTest) MarshalJSON() ([]byte, error) {
|
func (u *URLTest) MarshalJSON() ([]byte, error) {
|
||||||
all := []string{}
|
all := []string{}
|
||||||
@ -164,6 +199,7 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o
|
|||||||
}),
|
}),
|
||||||
fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10),
|
fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10),
|
||||||
disableUDP: option.DisableUDP,
|
disableUDP: option.DisableUDP,
|
||||||
|
testUrl: option.URL,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
|
@ -1,37 +1,10 @@
|
|||||||
package outboundgroup
|
package outboundgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) {
|
|
||||||
host, port, err := net.SplitHostPort(rawAddress)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("addrToMetadata failed: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip, err := netip.ParseAddr(host); err != nil {
|
|
||||||
addr = &C.Metadata{
|
|
||||||
Host: host,
|
|
||||||
DstPort: port,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addr = &C.Metadata{
|
|
||||||
Host: "",
|
|
||||||
DstIP: ip.Unmap(),
|
|
||||||
DstPort: port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func tcpKeepAlive(c net.Conn) {
|
func tcpKeepAlive(c net.Conn) {
|
||||||
if tcp, ok := c.(*net.TCPConn); ok {
|
if tcp, ok := c.(*net.TCPConn); ok {
|
||||||
_ = tcp.SetKeepAlive(true)
|
_ = tcp.SetKeepAlive(true)
|
||||||
@ -41,4 +14,5 @@ func tcpKeepAlive(c net.Conn) {
|
|||||||
|
|
||||||
type SelectAble interface {
|
type SelectAble interface {
|
||||||
Set(string) error
|
Set(string) error
|
||||||
|
ForceSet(name string)
|
||||||
}
|
}
|
||||||
|
@ -114,5 +114,19 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if muxMapping, muxExist := mapping["smux"].(map[string]any); muxExist {
|
||||||
|
muxOption := &outbound.SingMuxOption{}
|
||||||
|
err = decoder.Decode(muxMapping, muxOption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if muxOption.Enabled {
|
||||||
|
proxy, err = outbound.NewSingMux(*muxOption, proxy, proxy.(outbound.ProxyBase))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NewProxy(proxy), nil
|
return NewProxy(proxy), nil
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
"github.com/Dreamacro/clash/common/batch"
|
"github.com/Dreamacro/clash/common/batch"
|
||||||
"github.com/Dreamacro/clash/common/singledo"
|
"github.com/Dreamacro/clash/common/singledo"
|
||||||
"github.com/Dreamacro/clash/common/utils"
|
"github.com/Dreamacro/clash/common/utils"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
"go.uber.org/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -28,6 +28,7 @@ type proxyProviderSchema struct {
|
|||||||
Filter string `provider:"filter,omitempty"`
|
Filter string `provider:"filter,omitempty"`
|
||||||
ExcludeFilter string `provider:"exclude-filter,omitempty"`
|
ExcludeFilter string `provider:"exclude-filter,omitempty"`
|
||||||
ExcludeType string `provider:"exclude-type,omitempty"`
|
ExcludeType string `provider:"exclude-type,omitempty"`
|
||||||
|
DialerProxy string `provider:"dialer-proxy,omitempty"`
|
||||||
HealthCheck healthCheckSchema `provider:"health-check,omitempty"`
|
HealthCheck healthCheckSchema `provider:"health-check,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
|
|||||||
filter := schema.Filter
|
filter := schema.Filter
|
||||||
excludeFilter := schema.ExcludeFilter
|
excludeFilter := schema.ExcludeFilter
|
||||||
excludeType := schema.ExcludeType
|
excludeType := schema.ExcludeType
|
||||||
|
dialerProxy := schema.DialerProxy
|
||||||
|
|
||||||
return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, vehicle, hc)
|
return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, dialerProxy, vehicle, hc)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
types "github.com/Dreamacro/clash/constant/provider"
|
types "github.com/Dreamacro/clash/constant/provider"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
"github.com/Dreamacro/clash/tunnel/statistic"
|
||||||
|
|
||||||
"github.com/dlclark/regexp2"
|
"github.com/dlclark/regexp2"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@ -81,6 +82,7 @@ func (pp *proxySetProvider) Initial() error {
|
|||||||
}
|
}
|
||||||
pp.OnUpdate(elm)
|
pp.OnUpdate(elm)
|
||||||
pp.getSubscriptionInfo()
|
pp.getSubscriptionInfo()
|
||||||
|
pp.closeAllConnections()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,12 +140,24 @@ func (pp *proxySetProvider) getSubscriptionInfo() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pp *proxySetProvider) closeAllConnections() {
|
||||||
|
snapshot := statistic.DefaultManager.Snapshot()
|
||||||
|
for _, c := range snapshot.Connections {
|
||||||
|
for _, chain := range c.Chains() {
|
||||||
|
if chain == pp.Name() {
|
||||||
|
_ = c.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func stopProxyProvider(pd *ProxySetProvider) {
|
func stopProxyProvider(pd *ProxySetProvider) {
|
||||||
pd.healthCheck.close()
|
pd.healthCheck.close()
|
||||||
_ = pd.Fetcher.Destroy()
|
_ = pd.Fetcher.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
|
func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
|
||||||
excludeFilterReg, err := regexp2.Compile(excludeFilter, 0)
|
excludeFilterReg, err := regexp2.Compile(excludeFilter, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid excludeFilter regex: %w", err)
|
return nil, fmt.Errorf("invalid excludeFilter regex: %w", err)
|
||||||
@ -171,7 +185,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc
|
|||||||
healthCheck: hc,
|
healthCheck: hc,
|
||||||
}
|
}
|
||||||
|
|
||||||
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg), proxiesOnUpdate(pd))
|
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy), proxiesOnUpdate(pd))
|
||||||
pd.Fetcher = fetcher
|
pd.Fetcher = fetcher
|
||||||
wrapper := &ProxySetProvider{pd}
|
wrapper := &ProxySetProvider{pd}
|
||||||
runtime.SetFinalizer(wrapper, stopProxyProvider)
|
runtime.SetFinalizer(wrapper, stopProxyProvider)
|
||||||
@ -267,7 +281,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp) resource.Parser[[]C.Proxy] {
|
func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp, dialerProxy string) resource.Parser[[]C.Proxy] {
|
||||||
return func(buf []byte) ([]C.Proxy, error) {
|
return func(buf []byte) ([]C.Proxy, error) {
|
||||||
schema := &ProxySchema{}
|
schema := &ProxySchema{}
|
||||||
|
|
||||||
@ -330,6 +344,9 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray
|
|||||||
if _, ok := proxiesSet[name]; ok {
|
if _, ok := proxiesSet[name]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if len(dialerProxy) > 0 {
|
||||||
|
mapping["dialer-proxy"] = dialerProxy
|
||||||
|
}
|
||||||
proxy, err := adapter.ParseProxy(mapping)
|
proxy, err := adapter.ParseProxy(mapping)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("proxy %d error: %w", idx, err)
|
return nil, fmt.Errorf("proxy %d error: %w", idx, err)
|
||||||
|
205
common/atomic/type.go
Normal file
205
common/atomic/type.go
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bool struct {
|
||||||
|
atomic.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBool(val bool) *Bool {
|
||||||
|
i := &Bool{}
|
||||||
|
i.Store(val)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Bool) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Bool) UnmarshalJSON(b []byte) error {
|
||||||
|
var v bool
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Bool) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatBool(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pointer[T any] struct {
|
||||||
|
atomic.Pointer[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPointer[T any](v *T) *Pointer[T] {
|
||||||
|
var p Pointer[T]
|
||||||
|
if v != nil {
|
||||||
|
p.Store(v)
|
||||||
|
}
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pointer[T]) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(p.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pointer[T]) UnmarshalJSON(b []byte) error {
|
||||||
|
var v *T
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pointer[T]) String() string {
|
||||||
|
return fmt.Sprint(p.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
type Int32 struct {
|
||||||
|
atomic.Int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInt32(val int32) *Int32 {
|
||||||
|
i := &Int32{}
|
||||||
|
i.Store(val)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32) UnmarshalJSON(b []byte) error {
|
||||||
|
var v int32
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Int64 struct {
|
||||||
|
atomic.Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInt64(val int64) *Int64 {
|
||||||
|
i := &Int64{}
|
||||||
|
i.Store(val)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int64) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int64) UnmarshalJSON(b []byte) error {
|
||||||
|
var v int64
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int64) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uint32 struct {
|
||||||
|
atomic.Uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUint32(val uint32) *Uint32 {
|
||||||
|
i := &Uint32{}
|
||||||
|
i.Store(val)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uint32) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uint32) UnmarshalJSON(b []byte) error {
|
||||||
|
var v uint32
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uint32) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uint64 struct {
|
||||||
|
atomic.Uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUint64(val uint64) *Uint64 {
|
||||||
|
i := &Uint64{}
|
||||||
|
i.Store(val)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uint64) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uint64) UnmarshalJSON(b []byte) error {
|
||||||
|
var v uint64
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uint64) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uintptr struct {
|
||||||
|
atomic.Uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUintptr(val uintptr) *Uintptr {
|
||||||
|
i := &Uintptr{}
|
||||||
|
i.Store(val)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uintptr) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uintptr) UnmarshalJSON(b []byte) error {
|
||||||
|
var v uintptr
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uintptr) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
58
common/atomic/value.go
Normal file
58
common/atomic/value.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DefaultValue[T any]() T {
|
||||||
|
var defaultValue T
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypedValue[T any] struct {
|
||||||
|
value atomic.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypedValue[T]) Load() T {
|
||||||
|
value := t.value.Load()
|
||||||
|
if value == nil {
|
||||||
|
return DefaultValue[T]()
|
||||||
|
}
|
||||||
|
return value.(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypedValue[T]) Store(value T) {
|
||||||
|
t.value.Store(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypedValue[T]) Swap(new T) T {
|
||||||
|
old := t.value.Swap(new)
|
||||||
|
if old == nil {
|
||||||
|
return DefaultValue[T]()
|
||||||
|
}
|
||||||
|
return old.(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypedValue[T]) CompareAndSwap(old, new T) bool {
|
||||||
|
return t.value.CompareAndSwap(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypedValue[T]) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(t.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypedValue[T]) UnmarshalJSON(b []byte) error {
|
||||||
|
var v T
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTypedValue[T any](t T) *TypedValue[T] {
|
||||||
|
v := &TypedValue[T]{}
|
||||||
|
v.Store(t)
|
||||||
|
return v
|
||||||
|
}
|
@ -10,9 +10,11 @@ const BufferSize = buf.BufferSize
|
|||||||
type Buffer = buf.Buffer
|
type Buffer = buf.Buffer
|
||||||
|
|
||||||
var New = buf.New
|
var New = buf.New
|
||||||
|
var NewSize = buf.NewSize
|
||||||
var StackNew = buf.StackNew
|
var StackNew = buf.StackNew
|
||||||
var StackNewSize = buf.StackNewSize
|
var StackNewSize = buf.StackNewSize
|
||||||
var With = buf.With
|
var With = buf.With
|
||||||
|
var As = buf.As
|
||||||
|
|
||||||
var KeepAlive = common.KeepAlive
|
var KeepAlive = common.KeepAlive
|
||||||
|
|
||||||
@ -21,5 +23,7 @@ func Dup[T any](obj T) T {
|
|||||||
return common.Dup(obj)
|
return common.Dup(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
var Must = common.Must
|
var (
|
||||||
var Error = common.Error
|
Must = common.Must
|
||||||
|
Error = common.Error
|
||||||
|
)
|
||||||
|
@ -1,25 +1,55 @@
|
|||||||
package callback
|
package callback
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/Dreamacro/clash/common/buf"
|
||||||
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FirstWriteCallBackConn struct {
|
type firstWriteCallBackConn struct {
|
||||||
C.Conn
|
C.Conn
|
||||||
Callback func(error)
|
callback func(error)
|
||||||
written bool
|
written bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FirstWriteCallBackConn) Write(b []byte) (n int, err error) {
|
func (c *firstWriteCallBackConn) Write(b []byte) (n int, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if !c.written {
|
if !c.written {
|
||||||
c.written = true
|
c.written = true
|
||||||
c.Callback(err)
|
c.callback(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return c.Conn.Write(b)
|
return c.Conn.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FirstWriteCallBackConn) Upstream() any {
|
func (c *firstWriteCallBackConn) WriteBuffer(buffer *buf.Buffer) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if !c.written {
|
||||||
|
c.written = true
|
||||||
|
c.callback(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return c.Conn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *firstWriteCallBackConn) Upstream() any {
|
||||||
return c.Conn
|
return c.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *firstWriteCallBackConn) WriterReplaceable() bool {
|
||||||
|
return c.written
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *firstWriteCallBackConn) ReaderReplaceable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ N.ExtendedConn = (*firstWriteCallBackConn)(nil)
|
||||||
|
|
||||||
|
func NewFirstWriteCallBackConn(c C.Conn, callback func(error)) C.Conn {
|
||||||
|
return &firstWriteCallBackConn{
|
||||||
|
Conn: c,
|
||||||
|
callback: callback,
|
||||||
|
written: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,10 +5,11 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Dreamacro/clash/log"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config
|
// ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config
|
||||||
@ -201,7 +202,8 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
|||||||
vmess["servername"] = sni
|
vmess["servername"] = sni
|
||||||
}
|
}
|
||||||
|
|
||||||
network := strings.ToLower(values["net"].(string))
|
network, _ := values["net"].(string)
|
||||||
|
network = strings.ToLower(network)
|
||||||
if values["type"] == "http" {
|
if values["type"] == "http" {
|
||||||
network = "http"
|
network = "http"
|
||||||
} else if network == "http" {
|
} else if network == "http" {
|
||||||
@ -209,9 +211,12 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
|||||||
}
|
}
|
||||||
vmess["network"] = network
|
vmess["network"] = network
|
||||||
|
|
||||||
tls := strings.ToLower(values["tls"].(string))
|
tls, ok := values["tls"].(string)
|
||||||
if strings.HasSuffix(tls, "tls") {
|
if ok {
|
||||||
vmess["tls"] = true
|
tls = strings.ToLower(tls)
|
||||||
|
if strings.HasSuffix(tls, "tls") {
|
||||||
|
vmess["tls"] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch network {
|
switch network {
|
||||||
|
@ -69,6 +69,16 @@ func (c *BufferedConn) ReadBuffer(buffer *buf.Buffer) (err error) {
|
|||||||
return c.ExtendedConn.ReadBuffer(buffer)
|
return c.ExtendedConn.ReadBuffer(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BufferedConn) ReadCached() *buf.Buffer { // call in sing/common/bufio.Copy
|
||||||
|
if c.r.Buffered() > 0 {
|
||||||
|
length := c.r.Buffered()
|
||||||
|
b, _ := c.r.Peek(length)
|
||||||
|
_, _ = c.r.Discard(length)
|
||||||
|
return buf.As(b)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *BufferedConn) Upstream() any {
|
func (c *BufferedConn) Upstream() any {
|
||||||
return c.ExtendedConn
|
return c.ExtendedConn
|
||||||
}
|
}
|
||||||
@ -79,3 +89,7 @@ func (c *BufferedConn) ReaderReplaceable() bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BufferedConn) WriterReplaceable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@ -4,10 +4,12 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/buf"
|
||||||
)
|
)
|
||||||
|
|
||||||
type refConn struct {
|
type refConn struct {
|
||||||
conn net.Conn
|
conn ExtendedConn
|
||||||
ref any
|
ref any
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,8 +57,28 @@ func (c *refConn) Upstream() any {
|
|||||||
return c.conn
|
return c.conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *refConn) ReadBuffer(buffer *buf.Buffer) error {
|
||||||
|
defer runtime.KeepAlive(c.ref)
|
||||||
|
return c.conn.ReadBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *refConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
|
defer runtime.KeepAlive(c.ref)
|
||||||
|
return c.conn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *refConn) ReaderReplaceable() bool { // Relay() will handle reference
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *refConn) WriterReplaceable() bool { // Relay() will handle reference
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ExtendedConn = (*refConn)(nil)
|
||||||
|
|
||||||
func NewRefConn(conn net.Conn, ref any) net.Conn {
|
func NewRefConn(conn net.Conn, ref any) net.Conn {
|
||||||
return &refConn{conn: conn, ref: ref}
|
return &refConn{conn: NewExtendedConn(conn), ref: ref}
|
||||||
}
|
}
|
||||||
|
|
||||||
type refPacketConn struct {
|
type refPacketConn struct {
|
||||||
|
@ -3,9 +3,11 @@ package net
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
|
"github.com/sagernet/sing/common/bufio/deadline"
|
||||||
"github.com/sagernet/sing/common/network"
|
"github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,6 +19,14 @@ type ExtendedConn = network.ExtendedConn
|
|||||||
type ExtendedWriter = network.ExtendedWriter
|
type ExtendedWriter = network.ExtendedWriter
|
||||||
type ExtendedReader = network.ExtendedReader
|
type ExtendedReader = network.ExtendedReader
|
||||||
|
|
||||||
|
func NewDeadlineConn(conn net.Conn) ExtendedConn {
|
||||||
|
return deadline.NewFallbackConn(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeadlinePacketConn(pc net.PacketConn) net.PacketConn {
|
||||||
|
return deadline.NewFallbackPacketConn(bufio.NewPacketConn(pc))
|
||||||
|
}
|
||||||
|
|
||||||
func NeedHandshake(conn any) bool {
|
func NeedHandshake(conn any) bool {
|
||||||
if earlyConn, isEarlyConn := common.Cast[network.EarlyConn](conn); isEarlyConn && earlyConn.NeedHandshake() {
|
if earlyConn, isEarlyConn := common.Cast[network.EarlyConn](conn); isEarlyConn && earlyConn.NeedHandshake() {
|
||||||
return true
|
return true
|
||||||
@ -24,7 +34,11 @@ func NeedHandshake(conn any) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CountFunc = network.CountFunc
|
||||||
|
|
||||||
// Relay copies between left and right bidirectionally.
|
// Relay copies between left and right bidirectionally.
|
||||||
func Relay(leftConn, rightConn net.Conn) {
|
func Relay(leftConn, rightConn net.Conn) {
|
||||||
|
defer runtime.KeepAlive(leftConn)
|
||||||
|
defer runtime.KeepAlive(rightConn)
|
||||||
_ = bufio.CopyConn(context.TODO(), leftConn, rightConn)
|
_ = bufio.CopyConn(context.TODO(), leftConn, rightConn)
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"go.uber.org/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func iterator[T any](item []T) chan T {
|
func iterator[T any](item []T) chan T {
|
||||||
@ -44,7 +45,7 @@ func TestObservable_MultiSubscribe(t *testing.T) {
|
|||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
waitCh := func(ch <-chan int) {
|
waitCh := func(ch <-chan int) {
|
||||||
for range ch {
|
for range ch {
|
||||||
count.Inc()
|
count.Add(1)
|
||||||
}
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}
|
}
|
||||||
|
15
common/pool/buffer_low_memory.go
Normal file
15
common/pool/buffer_low_memory.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//go:build with_low_memory
|
||||||
|
|
||||||
|
package pool
|
||||||
|
|
||||||
|
const (
|
||||||
|
// io.Copy default buffer size is 32 KiB
|
||||||
|
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
||||||
|
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
||||||
|
RelayBufferSize = 16 * 1024
|
||||||
|
|
||||||
|
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
|
||||||
|
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
|
||||||
|
// set to 9000, so the UDP Buffer size set to 16Kib
|
||||||
|
UDPBufferSize = 8 * 1024
|
||||||
|
)
|
15
common/pool/buffer_standard.go
Normal file
15
common/pool/buffer_standard.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//go:build !with_low_memory
|
||||||
|
|
||||||
|
package pool
|
||||||
|
|
||||||
|
const (
|
||||||
|
// io.Copy default buffer size is 32 KiB
|
||||||
|
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
||||||
|
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
||||||
|
RelayBufferSize = 20 * 1024
|
||||||
|
|
||||||
|
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
|
||||||
|
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
|
||||||
|
// set to 9000, so the UDP Buffer size set to 16Kib
|
||||||
|
UDPBufferSize = 16 * 1024
|
||||||
|
)
|
@ -1,17 +1,5 @@
|
|||||||
package pool
|
package pool
|
||||||
|
|
||||||
const (
|
|
||||||
// io.Copy default buffer size is 32 KiB
|
|
||||||
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
|
||||||
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
|
||||||
RelayBufferSize = 20 * 1024
|
|
||||||
|
|
||||||
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
|
|
||||||
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
|
|
||||||
// set to 9000, so the UDP Buffer size set to 16Kib
|
|
||||||
UDPBufferSize = 16 * 1024
|
|
||||||
)
|
|
||||||
|
|
||||||
func Get(size int) []byte {
|
func Get(size int) []byte {
|
||||||
return defaultAllocator.Get(size)
|
return defaultAllocator.Get(size)
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"go.uber.org/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
func TestBasic(t *testing.T) {
|
||||||
@ -26,7 +27,7 @@ func TestBasic(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
_, _, shard := single.Do(call)
|
_, _, shard := single.Do(call)
|
||||||
if shard {
|
if shard {
|
||||||
shardCount.Inc()
|
shardCount.Add(1)
|
||||||
}
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
9
common/utils/strings.go
Normal file
9
common/utils/strings.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
func Reverse(s string) string {
|
||||||
|
a := []rune(s)
|
||||||
|
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
a[i], a[j] = a[j], a[i]
|
||||||
|
}
|
||||||
|
return string(a)
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid/v5"
|
||||||
"github.com/zhangyunhao116/fastrand"
|
"github.com/zhangyunhao116/fastrand"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid/v5"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ package dialer
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"syscall"
|
"syscall"
|
||||||
@ -20,11 +21,19 @@ func bind4(handle syscall.Handle, ifaceIdx int) error {
|
|||||||
var bytes [4]byte
|
var bytes [4]byte
|
||||||
binary.BigEndian.PutUint32(bytes[:], uint32(ifaceIdx))
|
binary.BigEndian.PutUint32(bytes[:], uint32(ifaceIdx))
|
||||||
idx := *(*uint32)(unsafe.Pointer(&bytes[0]))
|
idx := *(*uint32)(unsafe.Pointer(&bytes[0]))
|
||||||
return syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx))
|
err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("bind4: %w", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func bind6(handle syscall.Handle, ifaceIdx int) error {
|
func bind6(handle syscall.Handle, ifaceIdx int) error {
|
||||||
return syscall.SetsockoptInt(handle, syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, ifaceIdx)
|
err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, ifaceIdx)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("bind6: %w", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindControl(ifaceIdx int) controlFn {
|
func bindControl(ifaceIdx int) controlFn {
|
||||||
@ -49,9 +58,9 @@ func bindControl(ifaceIdx int) controlFn {
|
|||||||
if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil {
|
if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil {
|
||||||
// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
|
// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
|
||||||
if bind4err != nil {
|
if bind4err != nil {
|
||||||
innerErr = bind6err
|
innerErr = fmt.Errorf("%w (%s)", bind6err, bind4err)
|
||||||
} else {
|
} else {
|
||||||
innerErr = bind4err
|
innerErr = nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
innerErr = bind6err
|
innerErr = bind6err
|
||||||
|
@ -157,7 +157,7 @@ func concurrentDualStackDialContext(ctx context.Context, network string, ips []n
|
|||||||
}
|
}
|
||||||
|
|
||||||
func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) {
|
func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) {
|
||||||
ipv4s, ipv6s := sortationAddr(ips)
|
ipv4s, ipv6s := resolver.SortationAddr(ips)
|
||||||
preferIPVersion := opt.prefer
|
preferIPVersion := opt.prefer
|
||||||
|
|
||||||
fallbackTicker := time.NewTicker(fallbackTimeout)
|
fallbackTicker := time.NewTicker(fallbackTimeout)
|
||||||
@ -309,27 +309,16 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso
|
|||||||
return ips, port, nil
|
return ips, port, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) {
|
|
||||||
for _, v := range ips {
|
|
||||||
if v.Is4() { // 4in6 parse was in parseAddr
|
|
||||||
ipv4s = append(ipv4s, v)
|
|
||||||
} else {
|
|
||||||
ipv6s = append(ipv6s, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Dialer struct {
|
type Dialer struct {
|
||||||
opt option
|
Opt option
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
return DialContext(ctx, network, address, WithOption(d.opt))
|
return DialContext(ctx, network, address, WithOption(d.Opt))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
|
func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
|
||||||
opt := WithOption(d.opt)
|
opt := WithOption(d.Opt)
|
||||||
if rAddrPort.Addr().Unmap().IsLoopback() {
|
if rAddrPort.Addr().Unmap().IsLoopback() {
|
||||||
// avoid "The requested address is not valid in its context."
|
// avoid "The requested address is not valid in its context."
|
||||||
opt = WithInterface("")
|
opt = WithInterface("")
|
||||||
@ -339,5 +328,5 @@ func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddr
|
|||||||
|
|
||||||
func NewDialer(options ...Option) Dialer {
|
func NewDialer(options ...Option) Dialer {
|
||||||
opt := applyOptions(options...)
|
opt := applyOptions(options...)
|
||||||
return Dialer{opt: *opt}
|
return Dialer{Opt: *opt}
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
|
||||||
"go.uber.org/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultOptions []Option
|
DefaultOptions []Option
|
||||||
DefaultInterface = atomic.NewString("")
|
DefaultInterface = atomic.NewTypedValue[string]("")
|
||||||
DefaultRoutingMark = atomic.NewInt32(0)
|
DefaultRoutingMark = atomic.NewInt32(0)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -105,10 +105,22 @@ func (c *tfoConn) Upstream() any {
|
|||||||
return c.Conn
|
return c.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) NeedAdditionalReadDeadline() bool {
|
||||||
|
return c.Conn == nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *tfoConn) NeedHandshake() bool {
|
func (c *tfoConn) NeedHandshake() bool {
|
||||||
return c.Conn == nil
|
return c.Conn == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) ReaderReplaceable() bool {
|
||||||
|
return c.Conn != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tfoConn) WriterReplaceable() bool {
|
||||||
|
return c.Conn != nil
|
||||||
|
}
|
||||||
|
|
||||||
func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) {
|
func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false}
|
dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false}
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
package geodata
|
package geodata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Dreamacro/clash/component/mmdb"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
"github.com/Dreamacro/clash/log"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
clashHttp "github.com/Dreamacro/clash/component/http"
|
||||||
|
"github.com/Dreamacro/clash/component/mmdb"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var initGeoSite bool
|
var initGeoSite bool
|
||||||
@ -38,7 +42,9 @@ func InitGeoSite() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func downloadGeoSite(path string) (err error) {
|
func downloadGeoSite(path string) (err error) {
|
||||||
resp, err := http.Get(C.GeoSiteUrl)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||||
|
defer cancel()
|
||||||
|
resp, err := clashHttp.HttpRequest(ctx, C.GeoSiteUrl, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -55,7 +61,9 @@ func downloadGeoSite(path string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func downloadGeoIP(path string) (err error) {
|
func downloadGeoIP(path string) (err error) {
|
||||||
resp, err := http.Get(C.GeoIpUrl)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||||
|
defer cancel()
|
||||||
|
resp, err := clashHttp.HttpRequest(ctx, C.GeoIpUrl, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
package mmdb
|
package mmdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/oschwald/geoip2-golang"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
clashHttp "github.com/Dreamacro/clash/component/http"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
|
"github.com/oschwald/geoip2-golang"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -47,7 +51,9 @@ func Instance() *geoip2.Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DownloadMMDB(path string) (err error) {
|
func DownloadMMDB(path string) (err error) {
|
||||||
resp, err := http.Get(C.MmdbUrl)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||||
|
defer cancel()
|
||||||
|
resp, err := clashHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package profile
|
package profile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.uber.org/atomic"
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StoreSelected is a global switch for storing selected proxy to cache
|
// StoreSelected is a global switch for storing selected proxy to cache
|
||||||
|
96
component/proxydialer/proxydialer.go
Normal file
96
component/proxydialer/proxydialer.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package proxydialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/tunnel"
|
||||||
|
"github.com/Dreamacro/clash/tunnel/statistic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type proxyDialer struct {
|
||||||
|
proxy C.ProxyAdapter
|
||||||
|
dialer C.Dialer
|
||||||
|
statistic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(proxy C.ProxyAdapter, dialer C.Dialer, statistic bool) C.Dialer {
|
||||||
|
return proxyDialer{proxy: proxy, dialer: dialer, statistic: statistic}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewByName(proxyName string, dialer C.Dialer) (C.Dialer, error) {
|
||||||
|
proxies := tunnel.Proxies()
|
||||||
|
if proxy, ok := proxies[proxyName]; ok {
|
||||||
|
return New(proxy, dialer, true), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("proxyName[%s] not found", proxyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
currentMeta := &C.Metadata{Type: C.INNER}
|
||||||
|
if err := currentMeta.SetRemoteAddress(address); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if strings.Contains(network, "udp") { // using in wireguard outbound
|
||||||
|
if !currentMeta.Resolved() {
|
||||||
|
ip, err := resolver.ResolveIP(ctx, currentMeta.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("can't resolve ip")
|
||||||
|
}
|
||||||
|
currentMeta.DstIP = ip
|
||||||
|
}
|
||||||
|
pc, err := p.listenPacket(ctx, currentMeta)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil
|
||||||
|
}
|
||||||
|
var conn C.Conn
|
||||||
|
var err error
|
||||||
|
if d, ok := p.dialer.(dialer.Dialer); ok { // first using old function to let mux work
|
||||||
|
conn, err = p.proxy.DialContext(ctx, currentMeta, dialer.WithOption(d.Opt))
|
||||||
|
} else {
|
||||||
|
conn, err = p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if p.statistic {
|
||||||
|
conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, currentMeta, nil, 0, 0, false)
|
||||||
|
}
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
|
||||||
|
currentMeta := &C.Metadata{Type: C.INNER}
|
||||||
|
if err := currentMeta.SetRemoteAddress(address); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.listenPacket(ctx, currentMeta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) (C.PacketConn, error) {
|
||||||
|
var pc C.PacketConn
|
||||||
|
var err error
|
||||||
|
currentMeta.NetWork = C.UDP
|
||||||
|
if d, ok := p.dialer.(dialer.Dialer); ok { // first using old function to let mux work
|
||||||
|
pc, err = p.proxy.ListenPacketContext(ctx, currentMeta, dialer.WithOption(d.Opt))
|
||||||
|
} else {
|
||||||
|
pc, err = p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if p.statistic {
|
||||||
|
pc = statistic.NewUDPTracker(pc, statistic.DefaultManager, currentMeta, nil, 0, 0, false)
|
||||||
|
}
|
||||||
|
return pc, nil
|
||||||
|
}
|
@ -44,10 +44,8 @@ type Resolver interface {
|
|||||||
LookupIP(ctx context.Context, host string) (ips []netip.Addr, err error)
|
LookupIP(ctx context.Context, host string) (ips []netip.Addr, err error)
|
||||||
LookupIPv4(ctx context.Context, host string) (ips []netip.Addr, err error)
|
LookupIPv4(ctx context.Context, host string) (ips []netip.Addr, err error)
|
||||||
LookupIPv6(ctx context.Context, host string) (ips []netip.Addr, err error)
|
LookupIPv6(ctx context.Context, host string) (ips []netip.Addr, err error)
|
||||||
ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error)
|
|
||||||
ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error)
|
|
||||||
ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error)
|
|
||||||
ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error)
|
ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error)
|
||||||
|
Invalid() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupIPv4WithResolver same as LookupIPv4, but with a resolver
|
// LookupIPv4WithResolver same as LookupIPv4, but with a resolver
|
||||||
@ -68,7 +66,7 @@ func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]net
|
|||||||
return []netip.Addr{}, ErrIPVersion
|
return []netip.Addr{}, ErrIPVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
if r != nil {
|
if r != nil && r.Invalid() {
|
||||||
return r.LookupIPv4(ctx, host)
|
return r.LookupIPv4(ctx, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +122,7 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net
|
|||||||
return nil, ErrIPVersion
|
return nil, ErrIPVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
if r != nil {
|
if r != nil && r.Invalid() {
|
||||||
return r.LookupIPv6(ctx, host)
|
return r.LookupIPv6(ctx, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +162,7 @@ func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip
|
|||||||
return node.IPs, nil
|
return node.IPs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if r != nil {
|
if r != nil && r.Invalid() {
|
||||||
if DisableIPv6 {
|
if DisableIPv6 {
|
||||||
return r.LookupIPv4(ctx, host)
|
return r.LookupIPv4(ctx, host)
|
||||||
}
|
}
|
||||||
@ -200,10 +198,14 @@ func ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip.
|
|||||||
} else if len(ips) == 0 {
|
} else if len(ips) == 0 {
|
||||||
return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host)
|
return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host)
|
||||||
}
|
}
|
||||||
return ips[fastrand.Intn(len(ips))], nil
|
ipv4s, ipv6s := SortationAddr(ips)
|
||||||
|
if len(ipv4s) > 0 {
|
||||||
|
return ipv4s[fastrand.Intn(len(ipv4s))], nil
|
||||||
|
}
|
||||||
|
return ipv6s[fastrand.Intn(len(ipv6s))], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIP with a host, return ip
|
// ResolveIP with a host, return ip and priority return TypeA
|
||||||
func ResolveIP(ctx context.Context, host string) (netip.Addr, error) {
|
func ResolveIP(ctx context.Context, host string) (netip.Addr, error) {
|
||||||
return ResolveIPWithResolver(ctx, host, DefaultResolver)
|
return ResolveIPWithResolver(ctx, host, DefaultResolver)
|
||||||
}
|
}
|
||||||
@ -264,3 +266,14 @@ func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, er
|
|||||||
}
|
}
|
||||||
return LookupIP(ctx, host)
|
return LookupIP(ctx, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) {
|
||||||
|
for _, v := range ips {
|
||||||
|
if v.Unmap().Is4() {
|
||||||
|
ipv4s = append(ipv4s, v)
|
||||||
|
} else {
|
||||||
|
ipv6s = append(ipv6s, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -26,18 +26,18 @@ var (
|
|||||||
var Dispatcher *SnifferDispatcher
|
var Dispatcher *SnifferDispatcher
|
||||||
|
|
||||||
type SnifferDispatcher struct {
|
type SnifferDispatcher struct {
|
||||||
enable bool
|
enable bool
|
||||||
sniffers map[sniffer.Sniffer]SnifferConfig
|
sniffers map[sniffer.Sniffer]SnifferConfig
|
||||||
forceDomain *trie.DomainTrie[struct{}]
|
forceDomain *trie.DomainSet
|
||||||
skipSNI *trie.DomainTrie[struct{}]
|
skipSNI *trie.DomainSet
|
||||||
skipList *cache.LruCache[string, uint8]
|
skipList *cache.LruCache[string, uint8]
|
||||||
rwMux sync.RWMutex
|
rwMux sync.RWMutex
|
||||||
forceDnsMapping bool
|
forceDnsMapping bool
|
||||||
parsePureIp bool
|
parsePureIp bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) {
|
func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) {
|
||||||
if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Search(metadata.Host) != nil || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) {
|
if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Has(metadata.Host) || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) {
|
||||||
port, err := strconv.ParseUint(metadata.DstPort, 10, 16)
|
port, err := strconv.ParseUint(metadata.DstPort, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("[Sniffer] Dst port is error")
|
log.Debugln("[Sniffer] Dst port is error")
|
||||||
@ -74,7 +74,7 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
|
|||||||
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
|
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
if sd.skipSNI.Search(host) != nil {
|
if sd.skipSNI.Has(host) {
|
||||||
log.Debugln("[Sniffer] Skip sni[%s]", host)
|
log.Debugln("[Sniffer] Skip sni[%s]", host)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -166,8 +166,8 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
|
|||||||
return &dispatcher, nil
|
return &dispatcher, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig, forceDomain *trie.DomainTrie[struct{}],
|
func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig,
|
||||||
skipSNI *trie.DomainTrie[struct{}],
|
forceDomain *trie.DomainSet, skipSNI *trie.DomainSet,
|
||||||
forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
|
forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
|
||||||
dispatcher := SnifferDispatcher{
|
dispatcher := SnifferDispatcher{
|
||||||
enable: true,
|
enable: true,
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var trustCerts []*x509.Certificate
|
var trustCerts []*x509.Certificate
|
||||||
|
var certPool *x509.CertPool
|
||||||
var mutex sync.RWMutex
|
var mutex sync.RWMutex
|
||||||
var errNotMacth error = errors.New("certificate fingerprints do not match")
|
var errNotMacth error = errors.New("certificate fingerprints do not match")
|
||||||
|
|
||||||
@ -40,10 +40,20 @@ func ResetCertificate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getCertPool() *x509.CertPool {
|
func getCertPool() *x509.CertPool {
|
||||||
certPool, err := x509.SystemCertPool()
|
if len(trustCerts) == 0 {
|
||||||
if err == nil {
|
return nil
|
||||||
for _, cert := range trustCerts {
|
}
|
||||||
certPool.AddCert(cert)
|
if certPool == nil {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
if certPool != nil {
|
||||||
|
return certPool
|
||||||
|
}
|
||||||
|
certPool, err := x509.SystemCertPool()
|
||||||
|
if err == nil {
|
||||||
|
for _, cert := range trustCerts {
|
||||||
|
certPool.AddCert(cert)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return certPool
|
return certPool
|
||||||
|
@ -25,7 +25,7 @@ func ValidAndSplitDomain(domain string) ([]string, bool) {
|
|||||||
if domain != "" && domain[len(domain)-1] == '.' {
|
if domain != "" && domain[len(domain)-1] == '.' {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
domain = strings.ToLower(domain)
|
||||||
parts := strings.Split(domain, domainStep)
|
parts := strings.Split(domain, domainStep)
|
||||||
if len(parts) == 1 {
|
if len(parts) == 1 {
|
||||||
if parts[0] == "" {
|
if parts[0] == "" {
|
||||||
@ -123,6 +123,33 @@ func (t *DomainTrie[T]) Optimize() {
|
|||||||
t.root.optimize()
|
t.root.optimize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *DomainTrie[T]) Foreach(print func(domain string, data T)) {
|
||||||
|
for key, data := range t.root.getChildren() {
|
||||||
|
recursion([]string{key}, data, print)
|
||||||
|
if data != nil && data.inited {
|
||||||
|
print(joinDomain([]string{key}), data.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func recursion[T any](items []string, node *Node[T], fn func(domain string, data T)) {
|
||||||
|
for key, data := range node.getChildren() {
|
||||||
|
newItems := append([]string{key}, items...)
|
||||||
|
if data != nil && data.inited {
|
||||||
|
domain := joinDomain(newItems)
|
||||||
|
if domain[0] == domainStepByte {
|
||||||
|
domain = complexWildcard + domain
|
||||||
|
}
|
||||||
|
fn(domain, data.Data())
|
||||||
|
}
|
||||||
|
recursion(newItems, data, fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinDomain(items []string) string {
|
||||||
|
return strings.Join(items, domainStep)
|
||||||
|
}
|
||||||
|
|
||||||
// New returns a new, empty Trie.
|
// New returns a new, empty Trie.
|
||||||
func New[T any]() *DomainTrie[T] {
|
func New[T any]() *DomainTrie[T] {
|
||||||
return &DomainTrie[T]{root: newNode[T]()}
|
return &DomainTrie[T]{root: newNode[T]()}
|
||||||
|
174
component/trie/domain_set.go
Normal file
174
component/trie/domain_set.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package trie
|
||||||
|
|
||||||
|
// Package succinct provides several succinct data types.
|
||||||
|
// Modify from https://github.com/openacid/succinct/blob/d4684c35d123f7528b14e03c24327231723db704/sskv.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/utils"
|
||||||
|
"github.com/openacid/low/bitmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
complexWildcardByte = byte('+')
|
||||||
|
wildcardByte = byte('*')
|
||||||
|
domainStepByte = byte('.')
|
||||||
|
)
|
||||||
|
|
||||||
|
type DomainSet struct {
|
||||||
|
leaves, labelBitmap []uint64
|
||||||
|
labels []byte
|
||||||
|
ranks, selects []int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDomainSet creates a new *DomainSet struct, from a DomainTrie.
|
||||||
|
func (t *DomainTrie[T]) NewDomainSet() *DomainSet {
|
||||||
|
reserveDomains := make([]string, 0)
|
||||||
|
t.Foreach(func(domain string, data T) {
|
||||||
|
reserveDomains = append(reserveDomains, utils.Reverse(domain))
|
||||||
|
})
|
||||||
|
// ensure that the same prefix is continuous
|
||||||
|
// and according to the ascending sequence of length
|
||||||
|
sort.Strings(reserveDomains)
|
||||||
|
keys := reserveDomains
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ss := &DomainSet{}
|
||||||
|
lIdx := 0
|
||||||
|
|
||||||
|
type qElt struct{ s, e, col int }
|
||||||
|
queue := []qElt{{0, len(keys), 0}}
|
||||||
|
for i := 0; i < len(queue); i++ {
|
||||||
|
elt := queue[i]
|
||||||
|
if elt.col == len(keys[elt.s]) {
|
||||||
|
elt.s++
|
||||||
|
// a leaf node
|
||||||
|
setBit(&ss.leaves, i, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := elt.s; j < elt.e; {
|
||||||
|
|
||||||
|
frm := j
|
||||||
|
|
||||||
|
for ; j < elt.e && keys[j][elt.col] == keys[frm][elt.col]; j++ {
|
||||||
|
}
|
||||||
|
queue = append(queue, qElt{frm, j, elt.col + 1})
|
||||||
|
ss.labels = append(ss.labels, keys[frm][elt.col])
|
||||||
|
setBit(&ss.labelBitmap, lIdx, 0)
|
||||||
|
lIdx++
|
||||||
|
}
|
||||||
|
setBit(&ss.labelBitmap, lIdx, 1)
|
||||||
|
lIdx++
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.init()
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has query for a key and return whether it presents in the DomainSet.
|
||||||
|
func (ss *DomainSet) Has(key string) bool {
|
||||||
|
if ss == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
key = utils.Reverse(key)
|
||||||
|
key = strings.ToLower(key)
|
||||||
|
// no more labels in this node
|
||||||
|
// skip character matching
|
||||||
|
// go to next level
|
||||||
|
nodeId, bmIdx := 0, 0
|
||||||
|
type wildcardCursor struct {
|
||||||
|
bmIdx, index int
|
||||||
|
}
|
||||||
|
stack := make([]wildcardCursor, 0)
|
||||||
|
for i := 0; i < len(key); i++ {
|
||||||
|
RESTART:
|
||||||
|
c := key[i]
|
||||||
|
for ; ; bmIdx++ {
|
||||||
|
if getBit(ss.labelBitmap, bmIdx) != 0 {
|
||||||
|
if len(stack) > 0 {
|
||||||
|
cursor := stack[len(stack)-1]
|
||||||
|
stack = stack[0 : len(stack)-1]
|
||||||
|
// back wildcard and find next node
|
||||||
|
nextNodeId := countZeros(ss.labelBitmap, ss.ranks, cursor.bmIdx+1)
|
||||||
|
nextBmIdx := selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nextNodeId-1) + 1
|
||||||
|
j := cursor.index
|
||||||
|
for ; j < len(key) && key[j] != domainStepByte; j++ {
|
||||||
|
}
|
||||||
|
if j == len(key) {
|
||||||
|
if getBit(ss.leaves, nextNodeId) != 0 {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
goto RESTART
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ; ; nextBmIdx++ {
|
||||||
|
if nextBmIdx-nextNodeId < len(ss.labels) && ss.labels[nextBmIdx-nextNodeId] == domainStepByte {
|
||||||
|
bmIdx = nextBmIdx
|
||||||
|
nodeId = nextNodeId
|
||||||
|
i = j
|
||||||
|
goto RESTART
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// handle wildcard for domain
|
||||||
|
if ss.labels[bmIdx-nodeId] == complexWildcardByte {
|
||||||
|
return true
|
||||||
|
} else if ss.labels[bmIdx-nodeId] == wildcardByte {
|
||||||
|
cursor := wildcardCursor{}
|
||||||
|
cursor.bmIdx = bmIdx
|
||||||
|
cursor.index = i
|
||||||
|
stack = append(stack, cursor)
|
||||||
|
} else if ss.labels[bmIdx-nodeId] == c {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodeId = countZeros(ss.labelBitmap, ss.ranks, bmIdx+1)
|
||||||
|
bmIdx = selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nodeId-1) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return getBit(ss.leaves, nodeId) != 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBit(bm *[]uint64, i int, v int) {
|
||||||
|
for i>>6 >= len(*bm) {
|
||||||
|
*bm = append(*bm, 0)
|
||||||
|
}
|
||||||
|
(*bm)[i>>6] |= uint64(v) << uint(i&63)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBit(bm []uint64, i int) uint64 {
|
||||||
|
return bm[i>>6] & (1 << uint(i&63))
|
||||||
|
}
|
||||||
|
|
||||||
|
// init builds pre-calculated cache to speed up rank() and select()
|
||||||
|
func (ss *DomainSet) init() {
|
||||||
|
ss.selects, ss.ranks = bitmap.IndexSelect32R64(ss.labelBitmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// countZeros counts the number of "0" in a bitmap before the i-th bit(excluding
|
||||||
|
// the i-th bit) on behalf of rank index.
|
||||||
|
// E.g.:
|
||||||
|
//
|
||||||
|
// countZeros("010010", 4) == 3
|
||||||
|
// // 012345
|
||||||
|
func countZeros(bm []uint64, ranks []int32, i int) int {
|
||||||
|
a, _ := bitmap.Rank64(bm, ranks, int32(i))
|
||||||
|
return i - int(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// selectIthOne returns the index of the i-th "1" in a bitmap, on behalf of rank
|
||||||
|
// and select indexes.
|
||||||
|
// E.g.:
|
||||||
|
//
|
||||||
|
// selectIthOne("010010", 1) == 4
|
||||||
|
// // 012345
|
||||||
|
func selectIthOne(bm []uint64, ranks, selects []int32, i int) int {
|
||||||
|
a, _ := bitmap.Select32R64(bm, selects, ranks, int32(i))
|
||||||
|
return int(a)
|
||||||
|
}
|
85
component/trie/domain_set_test.go
Normal file
85
component/trie/domain_set_test.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package trie_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDomainSet(t *testing.T) {
|
||||||
|
tree := trie.New[struct{}]()
|
||||||
|
domainSet := []string{
|
||||||
|
"baidu.com",
|
||||||
|
"google.com",
|
||||||
|
"www.google.com",
|
||||||
|
"test.a.net",
|
||||||
|
"test.a.oc",
|
||||||
|
"Mijia Cloud",
|
||||||
|
".qq.com",
|
||||||
|
"+.cn",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, domain := range domainSet {
|
||||||
|
assert.NoError(t, tree.Insert(domain, struct{}{}))
|
||||||
|
}
|
||||||
|
set := tree.NewDomainSet()
|
||||||
|
assert.NotNil(t, set)
|
||||||
|
assert.True(t, set.Has("test.cn"))
|
||||||
|
assert.True(t, set.Has("cn"))
|
||||||
|
assert.True(t, set.Has("Mijia Cloud"))
|
||||||
|
assert.True(t, set.Has("test.a.net"))
|
||||||
|
assert.True(t, set.Has("www.qq.com"))
|
||||||
|
assert.True(t, set.Has("google.com"))
|
||||||
|
assert.False(t, set.Has("qq.com"))
|
||||||
|
assert.False(t, set.Has("www.baidu.com"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDomainSetComplexWildcard(t *testing.T) {
|
||||||
|
tree := trie.New[struct{}]()
|
||||||
|
domainSet := []string{
|
||||||
|
"+.baidu.com",
|
||||||
|
"+.a.baidu.com",
|
||||||
|
"www.baidu.com",
|
||||||
|
"+.bb.baidu.com",
|
||||||
|
"test.a.net",
|
||||||
|
"test.a.oc",
|
||||||
|
"www.qq.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, domain := range domainSet {
|
||||||
|
assert.NoError(t, tree.Insert(domain, struct{}{}))
|
||||||
|
}
|
||||||
|
set := tree.NewDomainSet()
|
||||||
|
assert.NotNil(t, set)
|
||||||
|
assert.False(t, set.Has("google.com"))
|
||||||
|
assert.True(t, set.Has("www.baidu.com"))
|
||||||
|
assert.True(t, set.Has("test.test.baidu.com"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDomainSetWildcard(t *testing.T) {
|
||||||
|
tree := trie.New[struct{}]()
|
||||||
|
domainSet := []string{
|
||||||
|
"*.*.*.baidu.com",
|
||||||
|
"www.baidu.*",
|
||||||
|
"stun.*.*",
|
||||||
|
"*.*.qq.com",
|
||||||
|
"test.*.baidu.com",
|
||||||
|
"*.apple.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, domain := range domainSet {
|
||||||
|
assert.NoError(t, tree.Insert(domain, struct{}{}))
|
||||||
|
}
|
||||||
|
set := tree.NewDomainSet()
|
||||||
|
assert.NotNil(t, set)
|
||||||
|
assert.True(t, set.Has("www.baidu.com"))
|
||||||
|
assert.True(t, set.Has("test.test.baidu.com"))
|
||||||
|
assert.True(t, set.Has("test.test.qq.com"))
|
||||||
|
assert.True(t, set.Has("stun.ab.cd"))
|
||||||
|
assert.False(t, set.Has("test.baidu.com"))
|
||||||
|
assert.False(t, set.Has("www.google.com"))
|
||||||
|
assert.False(t, set.Has("a.www.google.com"))
|
||||||
|
assert.False(t, set.Has("test.qq.com"))
|
||||||
|
assert.False(t, set.Has("test.test.test.qq.com"))
|
||||||
|
}
|
@ -1,16 +1,17 @@
|
|||||||
package trie
|
package trie_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var localIP = netip.AddrFrom4([4]byte{127, 0, 0, 1})
|
var localIP = netip.AddrFrom4([4]byte{127, 0, 0, 1})
|
||||||
|
|
||||||
func TestTrie_Basic(t *testing.T) {
|
func TestTrie_Basic(t *testing.T) {
|
||||||
tree := New[netip.Addr]()
|
tree := trie.New[netip.Addr]()
|
||||||
domains := []string{
|
domains := []string{
|
||||||
"example.com",
|
"example.com",
|
||||||
"google.com",
|
"google.com",
|
||||||
@ -18,7 +19,7 @@ func TestTrie_Basic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
tree.Insert(domain, localIP)
|
assert.NoError(t, tree.Insert(domain, localIP))
|
||||||
}
|
}
|
||||||
|
|
||||||
node := tree.Search("example.com")
|
node := tree.Search("example.com")
|
||||||
@ -31,7 +32,7 @@ func TestTrie_Basic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTrie_Wildcard(t *testing.T) {
|
func TestTrie_Wildcard(t *testing.T) {
|
||||||
tree := New[netip.Addr]()
|
tree := trie.New[netip.Addr]()
|
||||||
domains := []string{
|
domains := []string{
|
||||||
"*.example.com",
|
"*.example.com",
|
||||||
"sub.*.example.com",
|
"sub.*.example.com",
|
||||||
@ -47,7 +48,7 @@ func TestTrie_Wildcard(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
tree.Insert(domain, localIP)
|
assert.NoError(t, tree.Insert(domain, localIP))
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NotNil(t, tree.Search("sub.example.com"))
|
assert.NotNil(t, tree.Search("sub.example.com"))
|
||||||
@ -64,7 +65,7 @@ func TestTrie_Wildcard(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTrie_Priority(t *testing.T) {
|
func TestTrie_Priority(t *testing.T) {
|
||||||
tree := New[int]()
|
tree := trie.New[int]()
|
||||||
domains := []string{
|
domains := []string{
|
||||||
".dev",
|
".dev",
|
||||||
"example.dev",
|
"example.dev",
|
||||||
@ -79,7 +80,7 @@ func TestTrie_Priority(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for idx, domain := range domains {
|
for idx, domain := range domains {
|
||||||
tree.Insert(domain, idx+1)
|
assert.NoError(t, tree.Insert(domain, idx+1))
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFn("test.dev", 1)
|
assertFn("test.dev", 1)
|
||||||
@ -90,8 +91,8 @@ func TestTrie_Priority(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTrie_Boundary(t *testing.T) {
|
func TestTrie_Boundary(t *testing.T) {
|
||||||
tree := New[netip.Addr]()
|
tree := trie.New[netip.Addr]()
|
||||||
tree.Insert("*.dev", localIP)
|
assert.NoError(t, tree.Insert("*.dev", localIP))
|
||||||
|
|
||||||
assert.NotNil(t, tree.Insert(".", localIP))
|
assert.NotNil(t, tree.Insert(".", localIP))
|
||||||
assert.NotNil(t, tree.Insert("..dev", localIP))
|
assert.NotNil(t, tree.Insert("..dev", localIP))
|
||||||
@ -99,9 +100,29 @@ func TestTrie_Boundary(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTrie_WildcardBoundary(t *testing.T) {
|
func TestTrie_WildcardBoundary(t *testing.T) {
|
||||||
tree := New[netip.Addr]()
|
tree := trie.New[netip.Addr]()
|
||||||
tree.Insert("+.*", localIP)
|
assert.NoError(t, tree.Insert("+.*", localIP))
|
||||||
tree.Insert("stun.*.*.*", localIP)
|
assert.NoError(t, tree.Insert("stun.*.*.*", localIP))
|
||||||
|
|
||||||
assert.NotNil(t, tree.Search("example.com"))
|
assert.NotNil(t, tree.Search("example.com"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrie_Foreach(t *testing.T) {
|
||||||
|
tree := trie.New[netip.Addr]()
|
||||||
|
domainList := []string{
|
||||||
|
"google.com",
|
||||||
|
"stun.*.*.*",
|
||||||
|
"test.*.google.com",
|
||||||
|
"+.baidu.com",
|
||||||
|
"*.baidu.com",
|
||||||
|
"*.*.baidu.com",
|
||||||
|
}
|
||||||
|
for _, domain := range domainList {
|
||||||
|
assert.NoError(t, tree.Insert(domain, localIP))
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
tree.Foreach(func(domain string, data netip.Addr) {
|
||||||
|
count++
|
||||||
|
})
|
||||||
|
assert.Equal(t, 7, count)
|
||||||
|
}
|
||||||
|
@ -116,6 +116,18 @@ func (n *Node[T]) setData(data T) {
|
|||||||
n.inited = true
|
n.inited = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Node[T]) getChildren() map[string]*Node[T] {
|
||||||
|
if n.childMap == nil {
|
||||||
|
if n.childNode != nil {
|
||||||
|
m := make(map[string]*Node[T])
|
||||||
|
m[n.childStr] = n.childNode
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return n.childMap
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (n *Node[T]) Data() T {
|
func (n *Node[T]) Data() T {
|
||||||
return n.data
|
return n.data
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -136,9 +135,8 @@ type IPTables struct {
|
|||||||
type Sniffer struct {
|
type Sniffer struct {
|
||||||
Enable bool
|
Enable bool
|
||||||
Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig
|
Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig
|
||||||
Reverses *trie.DomainTrie[struct{}]
|
ForceDomain *trie.DomainSet
|
||||||
ForceDomain *trie.DomainTrie[struct{}]
|
SkipDomain *trie.DomainSet
|
||||||
SkipDomain *trie.DomainTrie[struct{}]
|
|
||||||
ForceDnsMapping bool
|
ForceDnsMapping bool
|
||||||
ParsePureIp bool
|
ParsePureIp bool
|
||||||
}
|
}
|
||||||
@ -490,7 +488,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||||||
}
|
}
|
||||||
config.Hosts = hosts
|
config.Hosts = hosts
|
||||||
|
|
||||||
dnsCfg, err := parseDNS(rawCfg, hosts, rules)
|
dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -822,8 +820,6 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s
|
|||||||
rules = append(rules, parsed)
|
rules = append(rules, parsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.GC()
|
|
||||||
|
|
||||||
return rules, nil
|
return rules, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +896,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
|||||||
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
|
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyAdapter := u.Fragment
|
proxyName := u.Fragment
|
||||||
|
|
||||||
var addr, dnsNetType string
|
var addr, dnsNetType string
|
||||||
params := map[string]string{}
|
params := map[string]string{}
|
||||||
@ -917,7 +913,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
|||||||
case "https":
|
case "https":
|
||||||
addr, err = hostWithDefaultPort(u.Host, "443")
|
addr, err = hostWithDefaultPort(u.Host, "443")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
proxyAdapter = ""
|
proxyName = ""
|
||||||
clearURL := url.URL{Scheme: "https", Host: addr, Path: u.Path}
|
clearURL := url.URL{Scheme: "https", Host: addr, Path: u.Path}
|
||||||
addr = clearURL.String()
|
addr = clearURL.String()
|
||||||
dnsNetType = "https" // DNS over HTTPS
|
dnsNetType = "https" // DNS over HTTPS
|
||||||
@ -927,7 +923,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
|||||||
if len(arr) == 0 {
|
if len(arr) == 0 {
|
||||||
continue
|
continue
|
||||||
} else if len(arr) == 1 {
|
} else if len(arr) == 1 {
|
||||||
proxyAdapter = arr[0]
|
proxyName = arr[0]
|
||||||
} else if len(arr) == 2 {
|
} else if len(arr) == 2 {
|
||||||
params[arr[0]] = arr[1]
|
params[arr[0]] = arr[1]
|
||||||
} else {
|
} else {
|
||||||
@ -953,18 +949,24 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
|||||||
nameservers = append(
|
nameservers = append(
|
||||||
nameservers,
|
nameservers,
|
||||||
dns.NameServer{
|
dns.NameServer{
|
||||||
Net: dnsNetType,
|
Net: dnsNetType,
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
ProxyAdapter: proxyAdapter,
|
ProxyName: proxyName,
|
||||||
Interface: dialer.DefaultInterface,
|
Interface: dialer.DefaultInterface,
|
||||||
Params: params,
|
Params: params,
|
||||||
PreferH3: preferH3,
|
PreferH3: preferH3,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return nameservers, nil
|
return nameservers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard
|
||||||
|
return parseNameServer(servers, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func parsePureDNSServer(server string) string {
|
func parsePureDNSServer(server string) string {
|
||||||
addPre := func(server string) string {
|
addPre := func(server string) string {
|
||||||
return "udp://" + server
|
return "udp://" + server
|
||||||
@ -983,7 +985,7 @@ func parsePureDNSServer(server string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][]dns.NameServer, error) {
|
func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]providerTypes.RuleProvider, preferH3 bool) (map[string][]dns.NameServer, error) {
|
||||||
policy := map[string][]dns.NameServer{}
|
policy := map[string][]dns.NameServer{}
|
||||||
updatedPolicy := make(map[string]interface{})
|
updatedPolicy := make(map[string]interface{})
|
||||||
re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`)
|
re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`)
|
||||||
@ -998,6 +1000,14 @@ func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][
|
|||||||
newKey := "geosite:" + subkey
|
newKey := "geosite:" + subkey
|
||||||
updatedPolicy[newKey] = v
|
updatedPolicy[newKey] = v
|
||||||
}
|
}
|
||||||
|
} else if strings.Contains(k, "rule-set:") {
|
||||||
|
subkeys := strings.Split(k, ":")
|
||||||
|
subkeys = subkeys[1:]
|
||||||
|
subkeys = strings.Split(subkeys[0], ",")
|
||||||
|
for _, subkey := range subkeys {
|
||||||
|
newKey := "rule-set:" + subkey
|
||||||
|
updatedPolicy[newKey] = v
|
||||||
|
}
|
||||||
} else if re.MatchString(k) {
|
} else if re.MatchString(k) {
|
||||||
subkeys := strings.Split(k, ",")
|
subkeys := strings.Split(k, ",")
|
||||||
for _, subkey := range subkeys {
|
for _, subkey := range subkeys {
|
||||||
@ -1021,6 +1031,19 @@ func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][
|
|||||||
if _, valid := trie.ValidAndSplitDomain(domain); !valid {
|
if _, valid := trie.ValidAndSplitDomain(domain); !valid {
|
||||||
return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain)
|
return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain)
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(domain, "rule-set:") {
|
||||||
|
domainSetName := domain[9:]
|
||||||
|
if provider, ok := ruleProviders[domainSetName]; !ok {
|
||||||
|
return nil, fmt.Errorf("not found rule-set: %s", domainSetName)
|
||||||
|
} else {
|
||||||
|
switch provider.Behavior() {
|
||||||
|
case providerTypes.IPCIDR:
|
||||||
|
return nil, fmt.Errorf("rule provider type error, except domain,actual %s", provider.Behavior())
|
||||||
|
case providerTypes.Classical:
|
||||||
|
log.Warnln("%s provider is %s, only matching it contain domain rule", provider.Name(), provider.Behavior())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
policy[domain] = nameservers
|
policy[domain] = nameservers
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1073,11 +1096,10 @@ func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]*router.DomainM
|
|||||||
log.Infoln("Start initial GeoSite dns fallback filter `%s`, records: %d", country, recordsCount)
|
log.Infoln("Start initial GeoSite dns fallback filter `%s`, records: %d", country, recordsCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runtime.GC()
|
|
||||||
return sites, nil
|
return sites, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule) (*DNS, error) {
|
func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) {
|
||||||
cfg := rawCfg.DNS
|
cfg := rawCfg.DNS
|
||||||
if cfg.Enable && len(cfg.NameServer) == 0 {
|
if cfg.Enable && len(cfg.NameServer) == 0 {
|
||||||
return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty")
|
return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty")
|
||||||
@ -1104,7 +1126,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, cfg.PreferH3); err != nil {
|
if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.PreferH3); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1324,23 +1346,24 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sniffer.Sniffers = loadSniffer
|
sniffer.Sniffers = loadSniffer
|
||||||
sniffer.ForceDomain = trie.New[struct{}]()
|
|
||||||
for _, domain := range snifferRaw.ForceDomain {
|
|
||||||
err := sniffer.ForceDomain.Insert(domain, struct{}{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sniffer.ForceDomain.Optimize()
|
|
||||||
|
|
||||||
sniffer.SkipDomain = trie.New[struct{}]()
|
forceDomainTrie := trie.New[struct{}]()
|
||||||
for _, domain := range snifferRaw.SkipDomain {
|
for _, domain := range snifferRaw.ForceDomain {
|
||||||
err := sniffer.SkipDomain.Insert(domain, struct{}{})
|
err := forceDomainTrie.Insert(domain, struct{}{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
|
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sniffer.SkipDomain.Optimize()
|
sniffer.ForceDomain = forceDomainTrie.NewDomainSet()
|
||||||
|
|
||||||
|
skipDomainTrie := trie.New[struct{}]()
|
||||||
|
for _, domain := range snifferRaw.SkipDomain {
|
||||||
|
err := skipDomainTrie.Insert(domain, struct{}{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sniffer.SkipDomain = skipDomainTrie.NewDomainSet()
|
||||||
|
|
||||||
return sniffer, nil
|
return sniffer, nil
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Dreamacro/clash/component/geodata"
|
|
||||||
_ "github.com/Dreamacro/clash/component/geodata/standard"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
"github.com/oschwald/geoip2-golang"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/component/geodata"
|
||||||
|
_ "github.com/Dreamacro/clash/component/geodata/standard"
|
||||||
|
clashHttp "github.com/Dreamacro/clash/component/http"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
|
||||||
|
"github.com/oschwald/geoip2-golang"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UpdateGeoDatabases() error {
|
func UpdateGeoDatabases() error {
|
||||||
@ -69,7 +74,9 @@ func UpdateGeoDatabases() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func downloadForBytes(url string) ([]byte, error) {
|
func downloadForBytes(url string) ([]byte, error) {
|
||||||
resp, err := http.Get(url)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||||
|
defer cancel()
|
||||||
|
resp, err := clashHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package config
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
||||||
@ -149,20 +150,11 @@ func proxyGroupsDagSort(groupsConfig []map[string]any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func verifyIP6() bool {
|
func verifyIP6() bool {
|
||||||
addrs, err := net.InterfaceAddrs()
|
if iAddrs, err := net.InterfaceAddrs(); err == nil {
|
||||||
if err != nil {
|
for _, addr := range iAddrs {
|
||||||
return false
|
if prefix, err := netip.ParsePrefix(addr.String()); err == nil {
|
||||||
}
|
if addr := prefix.Addr().Unmap(); addr.Is6() && addr.IsGlobalUnicast() {
|
||||||
for _, addr := range addrs {
|
return true
|
||||||
ipNet, isIpNet := addr.(*net.IPNet)
|
|
||||||
if isIpNet && !ipNet.IP.IsLoopback() {
|
|
||||||
if ipNet.IP.To16() != nil {
|
|
||||||
s := ipNet.IP.String()
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
switch s[i] {
|
|
||||||
case ':':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,14 @@ package constant
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,6 +45,8 @@ const (
|
|||||||
DefaultTLSTimeout = DefaultTCPTimeout
|
DefaultTLSTimeout = DefaultTCPTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrNotSupport = errors.New("no support")
|
||||||
|
|
||||||
type Connection interface {
|
type Connection interface {
|
||||||
Chains() Chain
|
Chains() Chain
|
||||||
AppendToChains(adapter ProxyAdapter)
|
AppendToChains(adapter ProxyAdapter)
|
||||||
@ -72,7 +76,7 @@ func (c Chain) Last() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Conn interface {
|
type Conn interface {
|
||||||
net.Conn
|
N.ExtendedConn
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,10 +120,13 @@ type ProxyAdapter interface {
|
|||||||
// SupportUOT return UDP over TCP support
|
// SupportUOT return UDP over TCP support
|
||||||
SupportUOT() bool
|
SupportUOT() bool
|
||||||
|
|
||||||
SupportWithDialer() bool
|
SupportWithDialer() NetWork
|
||||||
DialContextWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (Conn, error)
|
DialContextWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (Conn, error)
|
||||||
ListenPacketWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (PacketConn, error)
|
ListenPacketWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (PacketConn, error)
|
||||||
|
|
||||||
|
// IsL3Protocol return ProxyAdapter working in L3 (tell dns module not pass the domain to avoid loopback)
|
||||||
|
IsL3Protocol(metadata *Metadata) bool
|
||||||
|
|
||||||
// Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract.
|
// Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract.
|
||||||
Unwrap(metadata *Metadata, touch bool) Proxy
|
Unwrap(metadata *Metadata, touch bool) Proxy
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
N "github.com/Dreamacro/clash/common/net"
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PlainContext interface {
|
type PlainContext interface {
|
||||||
|
6
constant/features/low_memory.go
Normal file
6
constant/features/low_memory.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
//go:build with_low_memory
|
||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "with_low_memory")
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
//go:build no_doq
|
|
||||||
|
|
||||||
package features
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
TAGS = append(TAGS, "no_doq")
|
|
||||||
}
|
|
7
constant/features/no_fake_tcp.go
Normal file
7
constant/features/no_fake_tcp.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//go:build no_fake_tcp
|
||||||
|
|
||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "no_fake_tcp")
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
//go:build no_gvisor
|
|
||||||
|
|
||||||
package features
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
TAGS = append(TAGS, "no_gvisor")
|
|
||||||
}
|
|
7
constant/features/with_gvisor.go
Normal file
7
constant/features/with_gvisor.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//go:build with_gvisor
|
||||||
|
|
||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "with_gvisor")
|
||||||
|
}
|
@ -15,7 +15,10 @@ const (
|
|||||||
TCP NetWork = iota
|
TCP NetWork = iota
|
||||||
UDP
|
UDP
|
||||||
ALLNet
|
ALLNet
|
||||||
|
InvalidNet = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
HTTP Type = iota
|
HTTP Type = iota
|
||||||
HTTPS
|
HTTPS
|
||||||
SOCKS4
|
SOCKS4
|
||||||
@ -33,12 +36,16 @@ const (
|
|||||||
type NetWork int
|
type NetWork int
|
||||||
|
|
||||||
func (n NetWork) String() string {
|
func (n NetWork) String() string {
|
||||||
if n == TCP {
|
switch n {
|
||||||
|
case TCP:
|
||||||
return "tcp"
|
return "tcp"
|
||||||
} else if n == UDP {
|
case UDP:
|
||||||
return "udp"
|
return "udp"
|
||||||
|
case ALLNet:
|
||||||
|
return "all"
|
||||||
|
default:
|
||||||
|
return "invalid"
|
||||||
}
|
}
|
||||||
return "all"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n NetWork) MarshalJSON() ([]byte, error) {
|
func (n NetWork) MarshalJSON() ([]byte, error) {
|
||||||
@ -222,3 +229,21 @@ func (m *Metadata) String() string {
|
|||||||
func (m *Metadata) Valid() bool {
|
func (m *Metadata) Valid() bool {
|
||||||
return m.Host != "" || m.DstIP.IsValid()
|
return m.Host != "" || m.DstIP.IsValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Metadata) SetRemoteAddress(rawAddress string) error {
|
||||||
|
host, port, err := net.SplitHostPort(rawAddress)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip, err := netip.ParseAddr(host); err != nil {
|
||||||
|
m.Host = host
|
||||||
|
m.DstIP = netip.Addr{}
|
||||||
|
} else {
|
||||||
|
m.Host = ""
|
||||||
|
m.DstIP = ip.Unmap()
|
||||||
|
}
|
||||||
|
m.DstPort = port
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -73,17 +73,27 @@ type ProxyProvider interface {
|
|||||||
Version() uint32
|
Version() uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rule Type
|
// RuleProvider interface
|
||||||
|
type RuleProvider interface {
|
||||||
|
Provider
|
||||||
|
Behavior() RuleBehavior
|
||||||
|
Match(*constant.Metadata) bool
|
||||||
|
ShouldResolveIP() bool
|
||||||
|
ShouldFindProcess() bool
|
||||||
|
AsRule(adaptor string) constant.Rule
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule Behavior
|
||||||
const (
|
const (
|
||||||
Domain RuleType = iota
|
Domain RuleBehavior = iota
|
||||||
IPCIDR
|
IPCIDR
|
||||||
Classical
|
Classical
|
||||||
)
|
)
|
||||||
|
|
||||||
// RuleType defined
|
// RuleBehavior defined
|
||||||
type RuleType int
|
type RuleBehavior int
|
||||||
|
|
||||||
func (rt RuleType) String() string {
|
func (rt RuleBehavior) String() string {
|
||||||
switch rt {
|
switch rt {
|
||||||
case Domain:
|
case Domain:
|
||||||
return "Domain"
|
return "Domain"
|
||||||
@ -96,12 +106,20 @@ func (rt RuleType) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RuleProvider interface
|
const (
|
||||||
type RuleProvider interface {
|
YamlRule RuleFormat = iota
|
||||||
Provider
|
TextRule
|
||||||
Behavior() RuleType
|
)
|
||||||
Match(*constant.Metadata) bool
|
|
||||||
ShouldResolveIP() bool
|
type RuleFormat int
|
||||||
ShouldFindProcess() bool
|
|
||||||
AsRule(adaptor string) constant.Rule
|
func (rf RuleFormat) String() string {
|
||||||
|
switch rf {
|
||||||
|
case YamlRule:
|
||||||
|
return "YamlRule"
|
||||||
|
case TextRule:
|
||||||
|
return "TextRule"
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
N "github.com/Dreamacro/clash/common/net"
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConnContext struct {
|
type ConnContext struct {
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"github.com/Dreamacro/clash/common/utils"
|
"github.com/Dreamacro/clash/common/utils"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid/v5"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/Dreamacro/clash/common/utils"
|
"github.com/Dreamacro/clash/common/utils"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PacketConnContext struct {
|
type PacketConnContext struct {
|
||||||
|
@ -8,11 +8,11 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
"go.uber.org/atomic"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
|
||||||
D "github.com/miekg/dns"
|
D "github.com/miekg/dns"
|
||||||
"github.com/zhangyunhao116/fastrand"
|
"github.com/zhangyunhao116/fastrand"
|
||||||
@ -23,8 +23,9 @@ type client struct {
|
|||||||
r *Resolver
|
r *Resolver
|
||||||
port string
|
port string
|
||||||
host string
|
host string
|
||||||
iface *atomic.String
|
iface *atomic.TypedValue[string]
|
||||||
proxyAdapter string
|
proxyAdapter C.ProxyAdapter
|
||||||
|
proxyName string
|
||||||
addr string
|
addr string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +82,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
|
|||||||
options = append(options, dialer.WithInterface(c.iface.Load()))
|
options = append(options, dialer.WithInterface(c.iface.Load()))
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := getDialHandler(c.r, c.proxyAdapter, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port))
|
conn, err := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/atomic"
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/dhcp"
|
"github.com/Dreamacro/clash/component/dhcp"
|
||||||
"github.com/Dreamacro/clash/component/iface"
|
"github.com/Dreamacro/clash/component/iface"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
@ -86,7 +85,7 @@ func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) {
|
|||||||
for _, item := range dns {
|
for _, item := range dns {
|
||||||
nameserver = append(nameserver, NameServer{
|
nameserver = append(nameserver, NameServer{
|
||||||
Addr: net.JoinHostPort(item.String(), "53"),
|
Addr: net.JoinHostPort(item.String(), "53"),
|
||||||
Interface: atomic.NewString(d.ifaceName),
|
Interface: atomic.NewTypedValue(d.ifaceName),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
dns/doh.go
28
dns/doh.go
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/metacubex/quic-go"
|
"github.com/metacubex/quic-go"
|
||||||
"github.com/metacubex/quic-go/http3"
|
"github.com/metacubex/quic-go/http3"
|
||||||
D "github.com/miekg/dns"
|
D "github.com/miekg/dns"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -63,7 +64,8 @@ type dnsOverHTTPS struct {
|
|||||||
url *url.URL
|
url *url.URL
|
||||||
r *Resolver
|
r *Resolver
|
||||||
httpVersions []C.HTTPVersion
|
httpVersions []C.HTTPVersion
|
||||||
proxyAdapter string
|
proxyAdapter C.ProxyAdapter
|
||||||
|
proxyName string
|
||||||
addr string
|
addr string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +73,7 @@ type dnsOverHTTPS struct {
|
|||||||
var _ dnsClient = (*dnsOverHTTPS)(nil)
|
var _ dnsClient = (*dnsOverHTTPS)(nil)
|
||||||
|
|
||||||
// newDoH returns the DNS-over-HTTPS Upstream.
|
// newDoH returns the DNS-over-HTTPS Upstream.
|
||||||
func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter string) dnsClient {
|
func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) dnsClient {
|
||||||
u, _ := url.Parse(urlString)
|
u, _ := url.Parse(urlString)
|
||||||
httpVersions := DefaultHTTPVersions
|
httpVersions := DefaultHTTPVersions
|
||||||
if preferH3 {
|
if preferH3 {
|
||||||
@ -87,6 +89,7 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin
|
|||||||
addr: u.String(),
|
addr: u.String(),
|
||||||
r: r,
|
r: r,
|
||||||
proxyAdapter: proxyAdapter,
|
proxyAdapter: proxyAdapter,
|
||||||
|
proxyName: proxyName,
|
||||||
quicConfig: &quic.Config{
|
quicConfig: &quic.Config{
|
||||||
KeepAlivePeriod: QUICKeepAlivePeriod,
|
KeepAlivePeriod: QUICKeepAlivePeriod,
|
||||||
TokenStore: newQUICTokenStore(),
|
TokenStore: newQUICTokenStore(),
|
||||||
@ -390,14 +393,17 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
|
|||||||
nextProtos = append(nextProtos, string(v))
|
nextProtos = append(nextProtos, string(v))
|
||||||
}
|
}
|
||||||
tlsConfig.NextProtos = nextProtos
|
tlsConfig.NextProtos = nextProtos
|
||||||
dialContext := getDialHandler(doh.r, doh.proxyAdapter)
|
dialContext := getDialHandler(doh.r, doh.proxyAdapter, doh.proxyName)
|
||||||
// First, we attempt to create an HTTP3 transport. If the probe QUIC
|
|
||||||
// connection is established successfully, we'll be using HTTP3 for this
|
if slices.Contains(doh.httpVersions, C.HTTPVersion3) {
|
||||||
// upstream.
|
// First, we attempt to create an HTTP3 transport. If the probe QUIC
|
||||||
transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext)
|
// connection is established successfully, we'll be using HTTP3 for this
|
||||||
if err == nil {
|
// upstream.
|
||||||
log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String())
|
transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext)
|
||||||
return transportH3, nil
|
if err == nil {
|
||||||
|
log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String())
|
||||||
|
return transportH3, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugln("[%s] using HTTP/2 for this upstream: %v", doh.url.String(), err)
|
log.Debugln("[%s] using HTTP/2 for this upstream: %v", doh.url.String(), err)
|
||||||
@ -533,7 +539,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.
|
|||||||
IP: net.ParseIP(ip),
|
IP: net.ParseIP(ip),
|
||||||
Port: portInt,
|
Port: portInt,
|
||||||
}
|
}
|
||||||
conn, err := listenPacket(ctx, doh.proxyAdapter, "udp", addr, doh.r)
|
conn, err := listenPacket(ctx, doh.proxyAdapter, doh.proxyName, "udp", addr, doh.r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
15
dns/doq.go
15
dns/doq.go
@ -13,9 +13,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
"github.com/metacubex/quic-go"
|
"github.com/metacubex/quic-go"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/log"
|
|
||||||
D "github.com/miekg/dns"
|
D "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -60,7 +61,8 @@ type dnsOverQUIC struct {
|
|||||||
bytesPoolGuard sync.Mutex
|
bytesPoolGuard sync.Mutex
|
||||||
|
|
||||||
addr string
|
addr string
|
||||||
proxyAdapter string
|
proxyAdapter C.ProxyAdapter
|
||||||
|
proxyName string
|
||||||
r *Resolver
|
r *Resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,10 +70,11 @@ type dnsOverQUIC struct {
|
|||||||
var _ dnsClient = (*dnsOverQUIC)(nil)
|
var _ dnsClient = (*dnsOverQUIC)(nil)
|
||||||
|
|
||||||
// newDoQ returns the DNS-over-QUIC Upstream.
|
// newDoQ returns the DNS-over-QUIC Upstream.
|
||||||
func newDoQ(resolver *Resolver, addr string, adapter string) (dnsClient, error) {
|
func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) {
|
||||||
doq := &dnsOverQUIC{
|
doq := &dnsOverQUIC{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
proxyAdapter: adapter,
|
proxyAdapter: proxyAdapter,
|
||||||
|
proxyName: proxyName,
|
||||||
r: resolver,
|
r: resolver,
|
||||||
quicConfig: &quic.Config{
|
quicConfig: &quic.Config{
|
||||||
KeepAlivePeriod: QUICKeepAlivePeriod,
|
KeepAlivePeriod: QUICKeepAlivePeriod,
|
||||||
@ -310,7 +313,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
|
|||||||
// we're using bootstrapped address instead of what's passed to the function
|
// we're using bootstrapped address instead of what's passed to the function
|
||||||
// it does not create an actual connection, but it helps us determine
|
// it does not create an actual connection, but it helps us determine
|
||||||
// what IP is actually reachable (when there're v4/v6 addresses).
|
// what IP is actually reachable (when there're v4/v6 addresses).
|
||||||
rawConn, err := getDialHandler(doq.r, doq.proxyAdapter)(ctx, "udp", doq.addr)
|
rawConn, err := getDialHandler(doq.r, doq.proxyAdapter, doq.proxyName)(ctx, "udp", doq.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open a QUIC connection: %w", err)
|
return nil, fmt.Errorf("failed to open a QUIC connection: %w", err)
|
||||||
}
|
}
|
||||||
@ -325,7 +328,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
|
|||||||
|
|
||||||
p, err := strconv.Atoi(port)
|
p, err := strconv.Atoi(port)
|
||||||
udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p}
|
udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p}
|
||||||
udp, err := listenPacket(ctx, doq.proxyAdapter, "udp", addr, doq.r)
|
udp, err := listenPacket(ctx, doq.proxyAdapter, doq.proxyName, "udp", addr, doq.r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -3,23 +3,21 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/atomic"
|
"github.com/Dreamacro/clash/common/atomic"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
"github.com/Dreamacro/clash/common/cache"
|
||||||
"github.com/Dreamacro/clash/component/fakeip"
|
"github.com/Dreamacro/clash/component/fakeip"
|
||||||
"github.com/Dreamacro/clash/component/geodata/router"
|
"github.com/Dreamacro/clash/component/geodata/router"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
"github.com/Dreamacro/clash/component/trie"
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
D "github.com/miekg/dns"
|
D "github.com/miekg/dns"
|
||||||
"github.com/zhangyunhao116/fastrand"
|
|
||||||
"golang.org/x/sync/singleflight"
|
"golang.org/x/sync/singleflight"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,6 +38,11 @@ type geositePolicyRecord struct {
|
|||||||
inversedMatching bool
|
inversedMatching bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type domainSetPolicyRecord struct {
|
||||||
|
domainSetProvider provider.RuleProvider
|
||||||
|
policy *Policy
|
||||||
|
}
|
||||||
|
|
||||||
type Resolver struct {
|
type Resolver struct {
|
||||||
ipv6 bool
|
ipv6 bool
|
||||||
ipv6Timeout time.Duration
|
ipv6Timeout time.Duration
|
||||||
@ -51,6 +54,7 @@ type Resolver struct {
|
|||||||
group singleflight.Group
|
group singleflight.Group
|
||||||
lruCache *cache.LruCache[string, *D.Msg]
|
lruCache *cache.LruCache[string, *D.Msg]
|
||||||
policy *trie.DomainTrie[*Policy]
|
policy *trie.DomainTrie[*Policy]
|
||||||
|
domainSetPolicy []domainSetPolicyRecord
|
||||||
geositePolicy []geositePolicyRecord
|
geositePolicy []geositePolicyRecord
|
||||||
proxyServer []dnsClient
|
proxyServer []dnsClient
|
||||||
}
|
}
|
||||||
@ -93,7 +97,7 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr,
|
|||||||
|
|
||||||
ips, err = r.lookupIP(ctx, host, D.TypeA)
|
ips, err = r.lookupIP(ctx, host, D.TypeA)
|
||||||
var waitIPv6 *time.Timer
|
var waitIPv6 *time.Timer
|
||||||
if r != nil {
|
if r != nil && r.ipv6Timeout > 0 {
|
||||||
waitIPv6 = time.NewTimer(r.ipv6Timeout)
|
waitIPv6 = time.NewTimer(r.ipv6Timeout)
|
||||||
} else {
|
} else {
|
||||||
waitIPv6 = time.NewTimer(100 * time.Millisecond)
|
waitIPv6 = time.NewTimer(100 * time.Millisecond)
|
||||||
@ -112,49 +116,16 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr,
|
|||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
|
|
||||||
func (r *Resolver) ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error) {
|
|
||||||
ips, err := r.LookupIPPrimaryIPv4(ctx, host)
|
|
||||||
if err != nil {
|
|
||||||
return netip.Addr{}, err
|
|
||||||
} else if len(ips) == 0 {
|
|
||||||
return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
|
|
||||||
}
|
|
||||||
return ips[fastrand.Intn(len(ips))], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupIPv4 request with TypeA
|
// LookupIPv4 request with TypeA
|
||||||
func (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) {
|
func (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||||
return r.lookupIP(ctx, host, D.TypeA)
|
return r.lookupIP(ctx, host, D.TypeA)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv4 request with TypeA
|
|
||||||
func (r *Resolver) ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error) {
|
|
||||||
ips, err := r.lookupIP(ctx, host, D.TypeA)
|
|
||||||
if err != nil {
|
|
||||||
return netip.Addr{}, err
|
|
||||||
} else if len(ips) == 0 {
|
|
||||||
return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
|
|
||||||
}
|
|
||||||
return ips[fastrand.Intn(len(ips))], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupIPv6 request with TypeAAAA
|
// LookupIPv6 request with TypeAAAA
|
||||||
func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) {
|
func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||||
return r.lookupIP(ctx, host, D.TypeAAAA)
|
return r.lookupIP(ctx, host, D.TypeAAAA)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIPv6 request with TypeAAAA
|
|
||||||
func (r *Resolver) ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) {
|
|
||||||
ips, err := r.lookupIP(ctx, host, D.TypeAAAA)
|
|
||||||
if err != nil {
|
|
||||||
return netip.Addr{}, err
|
|
||||||
} else if len(ips) == 0 {
|
|
||||||
return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
|
|
||||||
}
|
|
||||||
return ips[fastrand.Intn(len(ips))], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
|
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
|
||||||
for _, filter := range r.fallbackIPFilters {
|
for _, filter := range r.fallbackIPFilters {
|
||||||
if filter.Match(ip) {
|
if filter.Match(ip) {
|
||||||
@ -301,6 +272,12 @@ func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
|
|||||||
return geositeRecord.policy.GetData()
|
return geositeRecord.policy.GetData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
metadata := &C.Metadata{Host: domain}
|
||||||
|
for _, domainSetRecord := range r.domainSetPolicy {
|
||||||
|
if ok := domainSetRecord.domainSetProvider.Match(metadata); ok {
|
||||||
|
return domainSetRecord.policy.GetData()
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,16 +376,20 @@ func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D
|
|||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasProxyServer has proxy server dns client
|
// Invalid return this resolver can or can't be used
|
||||||
func (r *Resolver) HasProxyServer() bool {
|
func (r *Resolver) Invalid() bool {
|
||||||
|
if r == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return len(r.main) > 0
|
return len(r.main) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type NameServer struct {
|
type NameServer struct {
|
||||||
Net string
|
Net string
|
||||||
Addr string
|
Addr string
|
||||||
Interface *atomic.String
|
Interface *atomic.TypedValue[string]
|
||||||
ProxyAdapter string
|
ProxyAdapter C.ProxyAdapter
|
||||||
|
ProxyName string
|
||||||
Params map[string]string
|
Params map[string]string
|
||||||
PreferH3 bool
|
PreferH3 bool
|
||||||
}
|
}
|
||||||
@ -422,16 +403,18 @@ type FallbackFilter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Main, Fallback []NameServer
|
Main, Fallback []NameServer
|
||||||
Default []NameServer
|
Default []NameServer
|
||||||
ProxyServer []NameServer
|
ProxyServer []NameServer
|
||||||
IPv6 bool
|
IPv6 bool
|
||||||
IPv6Timeout uint
|
IPv6Timeout uint
|
||||||
EnhancedMode C.DNSMode
|
EnhancedMode C.DNSMode
|
||||||
FallbackFilter FallbackFilter
|
FallbackFilter FallbackFilter
|
||||||
Pool *fakeip.Pool
|
Pool *fakeip.Pool
|
||||||
Hosts *trie.DomainTrie[resolver.HostValue]
|
Hosts *trie.DomainTrie[resolver.HostValue]
|
||||||
Policy map[string][]NameServer
|
Policy map[string][]NameServer
|
||||||
|
DomainSetPolicy map[provider.RuleProvider][]NameServer
|
||||||
|
GeositePolicy map[router.DomainMatcher][]NameServer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewResolver(config Config) *Resolver {
|
func NewResolver(config Config) *Resolver {
|
||||||
@ -483,6 +466,14 @@ func NewResolver(config Config) *Resolver {
|
|||||||
}
|
}
|
||||||
r.policy.Optimize()
|
r.policy.Optimize()
|
||||||
}
|
}
|
||||||
|
if len(config.DomainSetPolicy) > 0 {
|
||||||
|
for p, n := range config.DomainSetPolicy {
|
||||||
|
r.domainSetPolicy = append(r.domainSetPolicy, domainSetPolicyRecord{
|
||||||
|
domainSetProvider: p,
|
||||||
|
policy: NewPolicy(transform(n, defaultResolver)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fallbackIPFilters := []fallbackIPFilter{}
|
fallbackIPFilters := []fallbackIPFilter{}
|
||||||
if config.FallbackFilter.GeoIP {
|
if config.FallbackFilter.GeoIP {
|
||||||
@ -521,3 +512,5 @@ func NewProxyServerHostResolver(old *Resolver) *Resolver {
|
|||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ParseNameServer func(servers []string) ([]NameServer, error) // define in config/config.go
|
||||||
|
54
dns/util.go
54
dns/util.go
@ -74,13 +74,13 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
|
|||||||
for _, s := range servers {
|
for _, s := range servers {
|
||||||
switch s.Net {
|
switch s.Net {
|
||||||
case "https":
|
case "https":
|
||||||
ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter))
|
ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName))
|
||||||
continue
|
continue
|
||||||
case "dhcp":
|
case "dhcp":
|
||||||
ret = append(ret, newDHCPClient(s.Addr))
|
ret = append(ret, newDHCPClient(s.Addr))
|
||||||
continue
|
continue
|
||||||
case "quic":
|
case "quic":
|
||||||
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter); err == nil {
|
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter, s.ProxyName); err == nil {
|
||||||
ret = append(ret, doq)
|
ret = append(ret, doq)
|
||||||
} else {
|
} else {
|
||||||
log.Fatalln("DoQ format error: %v", err)
|
log.Fatalln("DoQ format error: %v", err)
|
||||||
@ -103,6 +103,7 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
|
|||||||
iface: s.Interface,
|
iface: s.Interface,
|
||||||
r: resolver,
|
r: resolver,
|
||||||
proxyAdapter: s.ProxyAdapter,
|
proxyAdapter: s.ProxyAdapter,
|
||||||
|
proxyName: s.ProxyName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
@ -144,9 +145,9 @@ func msgToDomain(msg *D.Msg) string {
|
|||||||
|
|
||||||
type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
|
type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||||
|
|
||||||
func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dialHandler {
|
func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler {
|
||||||
return func(ctx context.Context, network, addr string) (net.Conn, error) {
|
return func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
if len(proxyAdapter) == 0 {
|
if len(proxyName) == 0 && proxyAdapter == nil {
|
||||||
opts = append(opts, dialer.WithResolver(r))
|
opts = append(opts, dialer.WithResolver(r))
|
||||||
return dialer.DialContext(ctx, network, addr, opts...)
|
return dialer.DialContext(ctx, network, addr, opts...)
|
||||||
} else {
|
} else {
|
||||||
@ -154,10 +155,14 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
adapter, ok := tunnel.Proxies()[proxyAdapter]
|
if proxyAdapter == nil {
|
||||||
if !ok {
|
var ok bool
|
||||||
opts = append(opts, dialer.WithInterface(proxyAdapter))
|
proxyAdapter, ok = tunnel.Proxies()[proxyName]
|
||||||
|
if !ok {
|
||||||
|
opts = append(opts, dialer.WithInterface(proxyName))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(network, "tcp") {
|
if strings.Contains(network, "tcp") {
|
||||||
// tcp can resolve host by remote
|
// tcp can resolve host by remote
|
||||||
metadata := &C.Metadata{
|
metadata := &C.Metadata{
|
||||||
@ -165,8 +170,16 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia
|
|||||||
Host: host,
|
Host: host,
|
||||||
DstPort: port,
|
DstPort: port,
|
||||||
}
|
}
|
||||||
if ok {
|
if proxyAdapter != nil {
|
||||||
return adapter.DialContext(ctx, metadata, opts...)
|
if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback
|
||||||
|
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
metadata.Host = ""
|
||||||
|
metadata.DstIP = dstIP
|
||||||
|
}
|
||||||
|
return proxyAdapter.DialContext(ctx, metadata, opts...)
|
||||||
}
|
}
|
||||||
opts = append(opts, dialer.WithResolver(r))
|
opts = append(opts, dialer.WithResolver(r))
|
||||||
return dialer.DialContext(ctx, network, addr, opts...)
|
return dialer.DialContext(ctx, network, addr, opts...)
|
||||||
@ -182,15 +195,15 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia
|
|||||||
DstIP: dstIP,
|
DstIP: dstIP,
|
||||||
DstPort: port,
|
DstPort: port,
|
||||||
}
|
}
|
||||||
if !ok {
|
if proxyAdapter == nil {
|
||||||
return dialer.DialContext(ctx, network, addr, opts...)
|
return dialer.DialContext(ctx, network, addr, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !adapter.SupportUDP() {
|
if !proxyAdapter.SupportUDP() {
|
||||||
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...)
|
packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -201,14 +214,17 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenPacket(ctx context.Context, proxyAdapter string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
|
func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
|
||||||
host, port, err := net.SplitHostPort(addr)
|
host, port, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
adapter, ok := tunnel.Proxies()[proxyAdapter]
|
if proxyAdapter == nil {
|
||||||
if !ok && len(proxyAdapter) != 0 {
|
var ok bool
|
||||||
opts = append(opts, dialer.WithInterface(proxyAdapter))
|
proxyAdapter, ok = tunnel.Proxies()[proxyName]
|
||||||
|
if !ok {
|
||||||
|
opts = append(opts, dialer.WithInterface(proxyName))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// udp must resolve host first
|
// udp must resolve host first
|
||||||
@ -222,15 +238,15 @@ func listenPacket(ctx context.Context, proxyAdapter string, network string, addr
|
|||||||
DstIP: dstIP,
|
DstIP: dstIP,
|
||||||
DstPort: port,
|
DstPort: port,
|
||||||
}
|
}
|
||||||
if !ok {
|
if proxyAdapter == nil {
|
||||||
return dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...)
|
return dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !adapter.SupportUDP() {
|
if !proxyAdapter.SupportUDP() {
|
||||||
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
return adapter.ListenPacketContext(ctx, metadata, opts...)
|
return proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
|
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
|
||||||
|
2387
docs/allocs.svg
2387
docs/allocs.svg
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 127 KiB |
@ -238,6 +238,9 @@ dns:
|
|||||||
- https://doh.pub/dns-query
|
- https://doh.pub/dns-query
|
||||||
- https://dns.alidns.com/dns-query
|
- https://dns.alidns.com/dns-query
|
||||||
"www.baidu.com,+.google.cn": [223.5.5.5, https://dns.alidns.com/dns-query]
|
"www.baidu.com,+.google.cn": [223.5.5.5, https://dns.alidns.com/dns-query]
|
||||||
|
## global,dns 为 rule-providers 中的名为 global 和 dns 规则订阅,
|
||||||
|
## 且 behavior 必须为 domain/classical,当为 classical 时仅会生效域名类规则
|
||||||
|
# "rule-set:global,dns": 8.8.8.8
|
||||||
|
|
||||||
proxies: # socks5
|
proxies: # socks5
|
||||||
- name: "socks"
|
- name: "socks"
|
||||||
@ -299,6 +302,15 @@ proxies: # socks5
|
|||||||
# UDP 则为双栈解析,获取结果中的第一个 IPv4
|
# UDP 则为双栈解析,获取结果中的第一个 IPv4
|
||||||
# ipv6-prefer 同 ipv4-prefer
|
# ipv6-prefer 同 ipv4-prefer
|
||||||
# 现有协议都支持此参数,TCP 效果仅在开启 tcp-concurrent 生效
|
# 现有协议都支持此参数,TCP 效果仅在开启 tcp-concurrent 生效
|
||||||
|
smux:
|
||||||
|
enabled: false
|
||||||
|
protocol: smux # smux/yamux/h2mux
|
||||||
|
# max-connections: 4 # Maximum connections. Conflict with max-streams.
|
||||||
|
# min-streams: 4 # Minimum multiplexed streams in a connection before opening a new connection. Conflict with max-streams.
|
||||||
|
# max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams.
|
||||||
|
# padding: false # Enable padding. Requires sing-box server version 1.3-beta9 or later.
|
||||||
|
# statistic: false # 控制是否将底层连接显示在面板中,方便打断底层连接
|
||||||
|
# only-tcp: false # 如果设置为true, smux的设置将不会对udp生效,udp连接会直接走底层协议
|
||||||
|
|
||||||
- name: "ss2"
|
- name: "ss2"
|
||||||
type: ss
|
type: ss
|
||||||
@ -591,6 +603,7 @@ proxies: # socks5
|
|||||||
type: hysteria
|
type: hysteria
|
||||||
server: server.com
|
server: server.com
|
||||||
port: 443
|
port: 443
|
||||||
|
# ports: 1000,2000-3000,5000 # port 不可省略,
|
||||||
auth_str: yourpassword # 将会在未来某个时候删除
|
auth_str: yourpassword # 将会在未来某个时候删除
|
||||||
# auth-str: yourpassword
|
# auth-str: yourpassword
|
||||||
# obfs: obfs_str
|
# obfs: obfs_str
|
||||||
@ -619,12 +632,27 @@ proxies: # socks5
|
|||||||
port: 2480
|
port: 2480
|
||||||
ip: 172.16.0.2
|
ip: 172.16.0.2
|
||||||
ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5
|
ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5
|
||||||
private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU=
|
|
||||||
public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=
|
public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=
|
||||||
|
# pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=
|
||||||
|
private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU=
|
||||||
udp: true
|
udp: true
|
||||||
reserved: "U4An"
|
reserved: "U4An"
|
||||||
# 数组格式也是合法的
|
# 数组格式也是合法的
|
||||||
# reserved: [209,98,59]
|
# reserved: [209,98,59]
|
||||||
|
# 一个出站代理的标识。当值不为空时,将使用指定的 proxy 发出连接
|
||||||
|
# dialer-proxy: "ss1"
|
||||||
|
# remote-dns-resolve: true # 强制dns远程解析,默认值为false
|
||||||
|
# dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在remote-dns-resolve为true时生效
|
||||||
|
# 如果peers不为空,该段落中的allowed_ips不可为空;前面段落的server,port,ip,ipv6,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定
|
||||||
|
# peers:
|
||||||
|
# - server: 162.159.192.1
|
||||||
|
# port: 2480
|
||||||
|
# ip: 172.16.0.2
|
||||||
|
# ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5
|
||||||
|
# public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=
|
||||||
|
# # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=
|
||||||
|
# allowed_ips: ['0.0.0.0/0']
|
||||||
|
# reserved: [209,98,59]
|
||||||
|
|
||||||
# tuic
|
# tuic
|
||||||
- name: tuic
|
- name: tuic
|
||||||
@ -666,7 +694,9 @@ proxies: # socks5
|
|||||||
# protocol-param: "#"
|
# protocol-param: "#"
|
||||||
# udp: true
|
# udp: true
|
||||||
|
|
||||||
proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
|
proxy-groups:
|
||||||
|
# 代理链,目前relay可以支持udp的只有vmess/vless/trojan/ss/ssr/tuic
|
||||||
|
# wireguard目前不支持在relay中使用,请使用proxy中的dialer-proxy配置项
|
||||||
# Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet
|
# Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet
|
||||||
- name: "relay"
|
- name: "relay"
|
||||||
type: relay
|
type: relay
|
||||||
|
2182
docs/heap.svg
2182
docs/heap.svg
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 116 KiB |
65
go.mod
65
go.mod
@ -11,74 +11,97 @@ require (
|
|||||||
github.com/go-chi/chi/v5 v5.0.8
|
github.com/go-chi/chi/v5 v5.0.8
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-chi/render v1.0.2
|
github.com/go-chi/render v1.0.2
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid/v5 v5.0.0
|
||||||
github.com/google/gopacket v1.1.19
|
github.com/google/gopacket v1.1.19
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/hashicorp/golang-lru v0.5.4
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
|
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
|
||||||
github.com/jpillora/backoff v1.0.0
|
github.com/jpillora/backoff v1.0.0
|
||||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
|
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
|
||||||
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7
|
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7
|
||||||
github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594
|
github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594
|
||||||
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947
|
github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba
|
||||||
github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3
|
github.com/metacubex/sing-tun v0.1.4
|
||||||
github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb
|
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a
|
||||||
github.com/miekg/dns v1.1.52
|
github.com/miekg/dns v1.1.53
|
||||||
github.com/mroth/weightedrand/v2 v2.0.0
|
github.com/mroth/weightedrand/v2 v2.0.0
|
||||||
|
github.com/openacid/low v0.1.21
|
||||||
github.com/oschwald/geoip2-golang v1.8.0
|
github.com/oschwald/geoip2-golang v1.8.0
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
||||||
github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286
|
github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9
|
||||||
github.com/sagernet/sing-shadowtls v0.1.0
|
github.com/sagernet/sing-mux v0.0.0-20230425130511-b0a6ffd8406f
|
||||||
github.com/sagernet/sing-vmess v0.1.3
|
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b
|
||||||
|
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
|
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
|
||||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77
|
||||||
github.com/samber/lo v1.37.0
|
github.com/samber/lo v1.38.1
|
||||||
|
github.com/shirou/gopsutil/v3 v3.23.3
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.8.2
|
||||||
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837
|
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837
|
||||||
github.com/zhangyunhao116/fastrand v0.3.0
|
github.com/zhangyunhao116/fastrand v0.3.0
|
||||||
go.etcd.io/bbolt v1.3.6
|
go.etcd.io/bbolt v1.3.6
|
||||||
go.uber.org/atomic v1.10.0
|
go.uber.org/automaxprocs v1.5.2
|
||||||
go.uber.org/automaxprocs v1.5.1
|
golang.org/x/crypto v0.8.0
|
||||||
golang.org/x/crypto v0.7.0
|
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||||
golang.org/x/net v0.8.0
|
golang.org/x/net v0.9.0
|
||||||
golang.org/x/sync v0.1.0
|
golang.org/x/sync v0.1.0
|
||||||
golang.org/x/sys v0.6.0
|
golang.org/x/sys v0.7.0
|
||||||
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d
|
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
lukechampine.com/blake3 v1.1.7
|
lukechampine.com/blake3 v1.1.7
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/RyuaNerin/go-krypto v1.0.2 // indirect
|
||||||
|
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect
|
||||||
|
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
|
||||||
|
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
|
||||||
|
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/google/btree v1.0.1 // indirect
|
github.com/google/btree v1.0.1 // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||||
|
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||||
github.com/josharian/native v1.1.0 // indirect
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
github.com/klauspost/compress v1.15.15 // indirect
|
github.com/klauspost/compress v1.15.15 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
github.com/mdlayher/socket v0.4.0 // indirect
|
github.com/mdlayher/socket v0.4.0 // indirect
|
||||||
github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 // indirect
|
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c // indirect
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
|
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
|
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
|
github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
|
github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
|
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.5 // indirect
|
||||||
|
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
|
||||||
|
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
|
||||||
|
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||||
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/text v0.8.0 // indirect
|
golang.org/x/text v0.9.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace go.uber.org/atomic v1.10.0 => github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370
|
|
||||||
|
189
go.sum
189
go.sum
@ -1,11 +1,16 @@
|
|||||||
github.com/3andne/restls-client-go v0.1.4 h1:kLNC2aSRHPlEVYmTj6EOqJoorCpobEe2toMRSfBF7FU=
|
github.com/3andne/restls-client-go v0.1.4 h1:kLNC2aSRHPlEVYmTj6EOqJoorCpobEe2toMRSfBF7FU=
|
||||||
github.com/3andne/restls-client-go v0.1.4/go.mod h1:04CGbRk1BwBiEDles8b5mlKgTqIwE5MqF7JDloJV47I=
|
github.com/3andne/restls-client-go v0.1.4/go.mod h1:04CGbRk1BwBiEDles8b5mlKgTqIwE5MqF7JDloJV47I=
|
||||||
|
github.com/RyuaNerin/go-krypto v1.0.2 h1:9KiZrrBs+tDrQ66dNy4nrX6SzntKtSKdm0wKHhdB4WM=
|
||||||
|
github.com/RyuaNerin/go-krypto v1.0.2/go.mod h1:17LzMeJCgzGTkPH3TmfzRnEJ/yA7ErhTPp9sxIqONtA=
|
||||||
|
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok=
|
||||||
|
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
@ -18,7 +23,15 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
|
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
|
||||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
|
||||||
|
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
|
||||||
|
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
|
||||||
|
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po=
|
||||||
|
github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg=
|
||||||
|
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY=
|
||||||
|
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
|
||||||
|
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
|
||||||
|
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
|
||||||
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
@ -28,92 +41,96 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
|||||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||||
|
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
|
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
|
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
|
||||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
|
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
|
||||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
|
|
||||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
|
||||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
|
||||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
|
||||||
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
|
||||||
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcRuuPlZ2Jyf0Od8HLxOowi7CzKQqNtWn4=
|
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcRuuPlZ2Jyf0Od8HLxOowi7CzKQqNtWn4=
|
||||||
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ=
|
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ=
|
||||||
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
|
||||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
|
||||||
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
||||||
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
|
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
|
||||||
github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 h1:gREIdurac9fpyBMBRPPMF/Sk3gKfPfdNCa4GQyR9FoA=
|
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c h1:D62872jiuzC6b+3aI8tqfeyc6YgbfarYKywTnnvXwEM=
|
||||||
github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE=
|
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE=
|
||||||
github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw=
|
github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw=
|
||||||
github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA=
|
github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA=
|
||||||
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 h1:NnjC2+aIiyzzvFlo+C2WzBOJdsp+HAtu18FZomqYhUE=
|
github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba h1:He8YwyK600lHAS1xxNsP4k/jnZ8zqQ34XjCGn925+Yk=
|
||||||
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947/go.mod h1:U2gwhxzqgbhKCgn2B4z3t0Cj0LpMWFl/02BGCoG421w=
|
github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s=
|
||||||
github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3 h1:LnKcLs0HI0HX4xH/2XerX+1BLXS1Uj6Xvzn20xFuCOk=
|
github.com/metacubex/sing-tun v0.1.4 h1:OQDBNHjuPKrOprCiK+sLt97YQ0K6b9ZWmJB6z51ibZQ=
|
||||||
github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3/go.mod h1:0i22nk0tgkQz/N96hrhPib1O/C5AjxSnco7Mwi2YSF0=
|
github.com/metacubex/sing-tun v0.1.4/go.mod h1:BMfG00enVf90/CzcdX9PK3Dymgl7BZqHXJfexEyB7Cc=
|
||||||
github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb h1:uhvzbtOvyg2c1k1H2EeVPuPvTEjDHCq4+U0AljG40P8=
|
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a h1:cWKym33Qvl6HA3hj4/YuYD8hHyqQPb47wT5cJRAPgco=
|
||||||
github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM=
|
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY=
|
||||||
github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo=
|
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
|
||||||
github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||||
github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c=
|
|
||||||
github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
|
||||||
github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo=
|
github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo=
|
||||||
github.com/mroth/weightedrand/v2 v2.0.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
|
github.com/mroth/weightedrand/v2 v2.0.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=
|
||||||
|
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs=
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||||
|
github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=
|
||||||
|
github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=
|
||||||
|
github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=
|
||||||
|
github.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs3U1+I=
|
||||||
|
github.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=
|
||||||
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
|
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
|
||||||
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
|
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
@ -127,27 +144,44 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h
|
|||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286 h1:0Td2b5l1KgrdlOnbRWgFFWsyb0TLoq/tP6j9Lut4JN0=
|
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
|
github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9 h1:kpgKJbhesj6BBLTKIfBCJGQPm2ww7pNxn566C6TrHdA=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
|
github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
github.com/sagernet/sing-mux v0.0.0-20230425130511-b0a6ffd8406f h1:iEpOTgBTjt0vZJVXMTqYq13XyIu/337TWbq6WZ3CMWc=
|
||||||
github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM=
|
github.com/sagernet/sing-mux v0.0.0-20230425130511-b0a6ffd8406f/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
|
||||||
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE=
|
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
|
||||||
|
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
|
||||||
|
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
|
||||||
|
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
|
||||||
|
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||||
|
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg=
|
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg=
|
||||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=
|
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=
|
||||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
|
||||||
github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw=
|
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
|
||||||
github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA=
|
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
||||||
|
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
|
||||||
|
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||||
|
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=
|
||||||
|
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=
|
||||||
|
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=
|
||||||
|
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU=
|
||||||
|
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo=
|
||||||
|
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
@ -155,24 +189,31 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
|
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
|
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||||
|
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||||
|
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||||
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||||
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M=
|
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M=
|
||||||
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837/go.mod h1:YJTRELIWrGxR1s8xcEBgxcxBfwQfMGjdvNLTjN9XFgY=
|
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837/go.mod h1:YJTRELIWrGxR1s8xcEBgxcxBfwQfMGjdvNLTjN9XFgY=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig=
|
github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig=
|
||||||
github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc=
|
github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc=
|
||||||
|
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=
|
||||||
|
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=
|
||||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||||
go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk=
|
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
|
||||||
go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=
|
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
@ -180,56 +221,43 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||||
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
@ -243,7 +271,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
|||||||
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ=
|
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ=
|
||||||
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter"
|
"github.com/Dreamacro/clash/adapter"
|
||||||
@ -91,7 +92,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||||||
updateSniffer(cfg.Sniffer)
|
updateSniffer(cfg.Sniffer)
|
||||||
updateHosts(cfg.Hosts)
|
updateHosts(cfg.Hosts)
|
||||||
updateGeneral(cfg.General)
|
updateGeneral(cfg.General)
|
||||||
updateDNS(cfg.DNS, cfg.General.IPv6)
|
updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6)
|
||||||
updateListeners(cfg.General, cfg.Listeners, force)
|
updateListeners(cfg.General, cfg.Listeners, force)
|
||||||
updateIPTables(cfg)
|
updateIPTables(cfg)
|
||||||
updateTun(cfg.General)
|
updateTun(cfg.General)
|
||||||
@ -104,7 +105,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||||||
loadProxyProvider(cfg.Providers)
|
loadProxyProvider(cfg.Providers)
|
||||||
updateProfile(cfg)
|
updateProfile(cfg)
|
||||||
loadRuleProvider(cfg.RuleProviders)
|
loadRuleProvider(cfg.RuleProviders)
|
||||||
|
runtime.GC()
|
||||||
tunnel.OnRunning()
|
tunnel.OnRunning()
|
||||||
|
|
||||||
log.SetLevel(cfg.General.LogLevel)
|
log.SetLevel(cfg.General.LogLevel)
|
||||||
@ -175,10 +176,9 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateExperimental(c *config.Config) {
|
func updateExperimental(c *config.Config) {
|
||||||
runtime.GC()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateDNS(c *config.DNS, generalIPv6 bool) {
|
func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, generalIPv6 bool) {
|
||||||
if !c.Enable {
|
if !c.Enable {
|
||||||
resolver.DefaultResolver = nil
|
resolver.DefaultResolver = nil
|
||||||
resolver.DefaultHostMapper = nil
|
resolver.DefaultHostMapper = nil
|
||||||
@ -186,7 +186,25 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
|
|||||||
dns.ReCreateServer("", nil, nil)
|
dns.ReCreateServer("", nil, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
policy := make(map[string][]dns.NameServer)
|
||||||
|
domainSetPolicies := make(map[provider.RuleProvider][]dns.NameServer)
|
||||||
|
for key, nameservers := range c.NameServerPolicy {
|
||||||
|
temp := strings.Split(key, ":")
|
||||||
|
if len(temp) == 2 {
|
||||||
|
prefix := temp[0]
|
||||||
|
key := temp[1]
|
||||||
|
switch strings.ToLower(prefix) {
|
||||||
|
case "rule-set":
|
||||||
|
if p, ok := ruleProvider[key]; ok {
|
||||||
|
domainSetPolicies[p] = nameservers
|
||||||
|
}
|
||||||
|
case "geosite":
|
||||||
|
// TODO:
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
policy[key] = nameservers
|
||||||
|
}
|
||||||
|
}
|
||||||
cfg := dns.Config{
|
cfg := dns.Config{
|
||||||
Main: c.NameServer,
|
Main: c.NameServer,
|
||||||
Fallback: c.Fallback,
|
Fallback: c.Fallback,
|
||||||
@ -202,9 +220,10 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
|
|||||||
Domain: c.FallbackFilter.Domain,
|
Domain: c.FallbackFilter.Domain,
|
||||||
GeoSite: c.FallbackFilter.GeoSite,
|
GeoSite: c.FallbackFilter.GeoSite,
|
||||||
},
|
},
|
||||||
Default: c.DefaultNameserver,
|
Default: c.DefaultNameserver,
|
||||||
Policy: c.NameServerPolicy,
|
Policy: c.NameServerPolicy,
|
||||||
ProxyServer: c.ProxyServerNameserver,
|
ProxyServer: c.ProxyServerNameserver,
|
||||||
|
DomainSetPolicy: domainSetPolicies,
|
||||||
}
|
}
|
||||||
|
|
||||||
r := dns.NewResolver(cfg)
|
r := dns.NewResolver(cfg)
|
||||||
@ -220,7 +239,7 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
|
|||||||
resolver.DefaultHostMapper = m
|
resolver.DefaultHostMapper = m
|
||||||
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
|
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
|
||||||
|
|
||||||
if pr.HasProxyServer() {
|
if pr.Invalid() {
|
||||||
resolver.ProxyServerHostResolver = pr
|
resolver.ProxyServerHostResolver = pr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,7 +413,7 @@ func patchSelectGroup(proxies map[string]C.Proxy) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
selector.Set(selected)
|
selector.ForceSet(selected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/listener"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
@ -38,25 +39,29 @@ func restart(w http.ResponseWriter, r *http.Request) {
|
|||||||
// The background context is used because the underlying functions wrap it
|
// The background context is used because the underlying functions wrap it
|
||||||
// with timeout and shut down the server, which handles current request. It
|
// with timeout and shut down the server, which handles current request. It
|
||||||
// also should be done in a separate goroutine for the same reason.
|
// also should be done in a separate goroutine for the same reason.
|
||||||
go func() {
|
go runRestart(execPath)
|
||||||
if runtime.GOOS == "windows" {
|
}
|
||||||
cmd := exec.Command(execPath, os.Args[1:]...)
|
|
||||||
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
err = cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("restarting: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func runRestart(execPath string) {
|
||||||
|
var err error
|
||||||
|
listener.Cleanup(false)
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
cmd := exec.Command(execPath, os.Args[1:]...)
|
||||||
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
|
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
|
||||||
err = syscall.Exec(execPath, os.Args, os.Environ())
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("restarting: %s", err)
|
log.Fatalln("restarting: %s", err)
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
|
||||||
|
err = syscall.Exec(execPath, os.Args, os.Environ())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("restarting: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,11 @@ type Traffic struct {
|
|||||||
Down int64 `json:"down"`
|
Down int64 `json:"down"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Memory struct {
|
||||||
|
Inuse uint64 `json:"inuse"`
|
||||||
|
OSLimit uint64 `json:"oslimit"` // maybe we need it in the future
|
||||||
|
}
|
||||||
|
|
||||||
func SetUIPath(path string) {
|
func SetUIPath(path string) {
|
||||||
uiPath = C.Path.Resolve(path)
|
uiPath = C.Path.Resolve(path)
|
||||||
}
|
}
|
||||||
@ -76,6 +81,7 @@ func Start(addr string, tlsAddr string, secret string,
|
|||||||
r.Get("/", hello)
|
r.Get("/", hello)
|
||||||
r.Get("/logs", getLogs)
|
r.Get("/logs", getLogs)
|
||||||
r.Get("/traffic", traffic)
|
r.Get("/traffic", traffic)
|
||||||
|
r.Get("/memory", memory)
|
||||||
r.Get("/version", version)
|
r.Get("/version", version)
|
||||||
r.Mount("/configs", configRouter())
|
r.Mount("/configs", configRouter())
|
||||||
r.Mount("/proxies", proxyRouter())
|
r.Mount("/proxies", proxyRouter())
|
||||||
@ -224,6 +230,56 @@ func traffic(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func memory(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var wsConn *websocket.Conn
|
||||||
|
if websocket.IsWebSocketUpgrade(r) {
|
||||||
|
var err error
|
||||||
|
wsConn, err = upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if wsConn == nil {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
render.Status(r, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
tick := time.NewTicker(time.Second)
|
||||||
|
defer tick.Stop()
|
||||||
|
t := statistic.DefaultManager
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
var err error
|
||||||
|
first := true
|
||||||
|
for range tick.C {
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
inuse := t.Memory()
|
||||||
|
// make chat.js begin with zero
|
||||||
|
// this is shit var,but we need output 0 for first time
|
||||||
|
if first {
|
||||||
|
inuse = 0
|
||||||
|
first = false
|
||||||
|
}
|
||||||
|
if err := json.NewEncoder(buf).Encode(Memory{
|
||||||
|
Inuse: inuse,
|
||||||
|
OSLimit: 0,
|
||||||
|
}); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if wsConn == nil {
|
||||||
|
_, err = w.Write(buf.Bytes())
|
||||||
|
w.(http.Flusher).Flush()
|
||||||
|
} else {
|
||||||
|
err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Log struct {
|
type Log struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Payload string `json:"payload"`
|
Payload string `json:"payload"`
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package route
|
package route
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/hub/updater"
|
"github.com/Dreamacro/clash/hub/updater"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
func upgradeRouter() http.Handler {
|
func upgradeRouter() http.Handler {
|
||||||
@ -18,11 +21,24 @@ func upgradeRouter() http.Handler {
|
|||||||
func upgrade(w http.ResponseWriter, r *http.Request) {
|
func upgrade(w http.ResponseWriter, r *http.Request) {
|
||||||
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108
|
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108
|
||||||
log.Infoln("start update")
|
log.Infoln("start update")
|
||||||
err := updater.Update()
|
execPath, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("err:%s", err)
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
restart(w, r)
|
err = updater.Update(execPath)
|
||||||
|
if err != nil {
|
||||||
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
render.JSON(w, r, newError(fmt.Sprintf("Upgrade: %s", err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, r, render.M{"status": "ok"})
|
||||||
|
if f, ok := w.(http.Flusher); ok {
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
go runRestart(execPath)
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,12 @@ type updateError struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *updateError) Error() string {
|
func (e *updateError) Error() string {
|
||||||
return fmt.Sprintf("error: %s", e.Message)
|
return fmt.Sprintf("update error: %s", e.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update performs the auto-updater. It returns an error if the updater failed.
|
// Update performs the auto-updater. It returns an error if the updater failed.
|
||||||
// If firstRun is true, it assumes the configuration file doesn't exist.
|
// If firstRun is true, it assumes the configuration file doesn't exist.
|
||||||
func Update() (err error) {
|
func Update(execPath string) (err error) {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
|
|
||||||
@ -63,11 +63,10 @@ func Update() (err error) {
|
|||||||
goarch = runtime.GOARCH
|
goarch = runtime.GOARCH
|
||||||
latestVersion, err = getLatestVersion()
|
latestVersion, err = getLatestVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := &updateError{Message: err.Error()}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infoln("current version alpha-%s, latest version alpha-%s", constant.Version, latestVersion)
|
log.Infoln("current version %s, latest version %s", constant.Version, latestVersion)
|
||||||
|
|
||||||
if latestVersion == constant.Version {
|
if latestVersion == constant.Version {
|
||||||
err := &updateError{Message: "Already using latest version"}
|
err := &updateError{Message: "Already using latest version"}
|
||||||
@ -84,11 +83,6 @@ func Update() (err error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
execPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("getting executable path: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
workDir = filepath.Dir(execPath)
|
workDir = filepath.Dir(execPath)
|
||||||
|
|
||||||
err = prepare(execPath)
|
err = prepare(execPath)
|
||||||
@ -110,7 +104,7 @@ func Update() (err error) {
|
|||||||
|
|
||||||
err = backup()
|
err = backup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("replacing: %w", err)
|
return fmt.Errorf("backuping: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = replace()
|
err = replace()
|
||||||
@ -134,8 +128,10 @@ func prepare(exePath string) (err error) {
|
|||||||
//log.Infoln(packageName)
|
//log.Infoln(packageName)
|
||||||
backupDir = filepath.Join(workDir, "meta-backup")
|
backupDir = filepath.Join(workDir, "meta-backup")
|
||||||
|
|
||||||
if goos == "windows" {
|
if goos == "windows" && goarch == "amd64" {
|
||||||
updateExeName = "clash.meta" + "-" + goos + "-" + goarch + ".exe"
|
updateExeName = "clash.meta" + "-" + goos + "-" + goarch + "-compatible.exe"
|
||||||
|
} else if goos == "linux" && goarch == "amd64" {
|
||||||
|
updateExeName = "clash.meta" + "-" + goos + "-" + goarch + "-compatible"
|
||||||
} else {
|
} else {
|
||||||
updateExeName = "clash.meta" + "-" + goos + "-" + goarch
|
updateExeName = "clash.meta" + "-" + goos + "-" + goarch
|
||||||
}
|
}
|
||||||
@ -164,7 +160,7 @@ func unpack() error {
|
|||||||
var err error
|
var err error
|
||||||
_, pkgNameOnly := filepath.Split(packageURL)
|
_, pkgNameOnly := filepath.Split(packageURL)
|
||||||
|
|
||||||
log.Debugln("updater: unpacking package")
|
log.Infoln("updater: unpacking package")
|
||||||
if strings.HasSuffix(pkgNameOnly, ".zip") {
|
if strings.HasSuffix(pkgNameOnly, ".zip") {
|
||||||
_, err = zipFileUnpack(packageName, updateDir)
|
_, err = zipFileUnpack(packageName, updateDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -184,43 +180,36 @@ func unpack() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// backup makes a backup of the current configuration and supporting files. It
|
// backup makes a backup of the current executable file
|
||||||
// ignores the configuration file if firstRun is true.
|
|
||||||
func backup() (err error) {
|
func backup() (err error) {
|
||||||
log.Infoln("updater: backing up current Exefile")
|
log.Infoln("updater: backing up current ExecFile:%s to %s", currentExeName, backupExeName)
|
||||||
_ = os.Mkdir(backupDir, 0o755)
|
_ = os.Mkdir(backupDir, 0o755)
|
||||||
|
|
||||||
err = copyFile(currentExeName, backupExeName)
|
err = os.Rename(currentExeName, backupExeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", currentExeName, backupExeName, err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace moves the current executable with the updated one and also copies the
|
// replace moves the current executable with the updated one
|
||||||
// supporting files.
|
|
||||||
func replace() error {
|
func replace() error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName)
|
log.Infoln("replacing: %s to %s", updateExeName, currentExeName)
|
||||||
// err := os.Rename(currentExeName, backupExeName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
|
|
||||||
if goos == "windows" {
|
if goos == "windows" {
|
||||||
// rename fails with "File in use" error
|
// rename fails with "File in use" error
|
||||||
log.Infoln("copying: %s to %s", updateExeName, currentExeName)
|
|
||||||
err = copyFile(updateExeName, currentExeName)
|
err = copyFile(updateExeName, currentExeName)
|
||||||
} else {
|
} else {
|
||||||
log.Infoln("copying: %s to %s", updateExeName, currentExeName)
|
|
||||||
err = os.Rename(updateExeName, currentExeName)
|
err = os.Rename(updateExeName, currentExeName)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infoln("updater: renamed: %s to %s", updateExeName, currentExeName)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,8 +225,6 @@ const MaxPackageFileSize = 32 * 1024 * 1024
|
|||||||
|
|
||||||
// Download package file and save it to disk
|
// Download package file and save it to disk
|
||||||
func downloadPackageFile() (err error) {
|
func downloadPackageFile() (err error) {
|
||||||
// var resp *http.Response
|
|
||||||
// resp, err = client.Get(packageURL)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
resp, err := clashHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
|
resp, err := clashHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
|
||||||
@ -447,6 +434,8 @@ func updateDownloadURL() {
|
|||||||
middle = fmt.Sprintf("-%s-%sv%s-%s", goos, goarch, goarm, latestVersion)
|
middle = fmt.Sprintf("-%s-%sv%s-%s", goos, goarch, goarm, latestVersion)
|
||||||
} else if isMIPS(goarch) && gomips != "" {
|
} else if isMIPS(goarch) && gomips != "" {
|
||||||
middle = fmt.Sprintf("-%s-%s-%s-%s", goos, goarch, gomips, latestVersion)
|
middle = fmt.Sprintf("-%s-%s-%s-%s", goos, goarch, gomips, latestVersion)
|
||||||
|
} else if goarch == "amd64" && (goos == "windows" || goos == "linux") {
|
||||||
|
middle = fmt.Sprintf("-%s-%s-compatible-%s", goos, goarch, latestVersion)
|
||||||
} else {
|
} else {
|
||||||
middle = fmt.Sprintf("-%s-%s-%s", goos, goarch, latestVersion)
|
middle = fmt.Sprintf("-%s-%s-%s", goos, goarch, latestVersion)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
LC "github.com/Dreamacro/clash/listener/config"
|
LC "github.com/Dreamacro/clash/listener/config"
|
||||||
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
||||||
@ -100,6 +101,7 @@ func (l *Listener) AddrList() (addrList []net.Addr) {
|
|||||||
|
|
||||||
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
||||||
conn = l.pickCipher.StreamConn(conn)
|
conn = l.pickCipher.StreamConn(conn)
|
||||||
|
conn = N.NewDeadlineConn(conn) // embed ss can't handle readDeadline correctly
|
||||||
|
|
||||||
target, err := socks5.ReadAddr(conn, make([]byte, socks5.MaxAddrLen))
|
target, err := socks5.ReadAddr(conn, make([]byte, socks5.MaxAddrLen))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -10,12 +10,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter/inbound"
|
"github.com/Dreamacro/clash/adapter/inbound"
|
||||||
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
"github.com/Dreamacro/clash/transport/socks5"
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
|
|
||||||
|
mux "github.com/sagernet/sing-mux"
|
||||||
vmess "github.com/sagernet/sing-vmess"
|
vmess "github.com/sagernet/sing-vmess"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/bufio/deadline"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
"github.com/sagernet/sing/common/network"
|
"github.com/sagernet/sing/common/network"
|
||||||
@ -33,7 +36,7 @@ type ListenerHandler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type waitCloseConn struct {
|
type waitCloseConn struct {
|
||||||
net.Conn
|
N.ExtendedConn
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
close sync.Once
|
close sync.Once
|
||||||
rAddr net.Addr
|
rAddr net.Addr
|
||||||
@ -43,7 +46,7 @@ func (c *waitCloseConn) Close() error { // call from handleTCPConn(connCtx C.Con
|
|||||||
c.close.Do(func() {
|
c.close.Do(func() {
|
||||||
c.wg.Done()
|
c.wg.Done()
|
||||||
})
|
})
|
||||||
return c.Conn.Close()
|
return c.ExtendedConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *waitCloseConn) RemoteAddr() net.Addr {
|
func (c *waitCloseConn) RemoteAddr() net.Addr {
|
||||||
@ -51,7 +54,14 @@ func (c *waitCloseConn) RemoteAddr() net.Addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *waitCloseConn) Upstream() any {
|
func (c *waitCloseConn) Upstream() any {
|
||||||
return c.Conn
|
return c.ExtendedConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpstreamMetadata(metadata M.Metadata) M.Metadata {
|
||||||
|
return M.Metadata{
|
||||||
|
Source: metadata.Source,
|
||||||
|
Destination: metadata.Destination,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
@ -61,6 +71,8 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
|
|||||||
additions = append(additions, ctxAdditions...)
|
additions = append(additions, ctxAdditions...)
|
||||||
}
|
}
|
||||||
switch metadata.Destination.Fqdn {
|
switch metadata.Destination.Fqdn {
|
||||||
|
case mux.Destination.Fqdn:
|
||||||
|
return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata))
|
||||||
case vmess.MuxDestination.Fqdn:
|
case vmess.MuxDestination.Fqdn:
|
||||||
return vmess.HandleMuxConnection(ctx, conn, h)
|
return vmess.HandleMuxConnection(ctx, conn, h)
|
||||||
case uot.MagicAddress:
|
case uot.MagicAddress:
|
||||||
@ -79,7 +91,10 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
|
|||||||
defer wg.Wait() // this goroutine must exit after conn.Close()
|
defer wg.Wait() // this goroutine must exit after conn.Close()
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{Conn: conn, wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...)
|
if deadline.NeedAdditionalReadDeadline(conn) {
|
||||||
|
conn = N.NewDeadlineConn(conn) // conn from sing should check NeedAdditionalReadDeadline
|
||||||
|
}
|
||||||
|
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{ExtendedConn: N.NewExtendedConn(conn), wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +117,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
|||||||
dest, err := conn.ReadPacket(buff)
|
dest, err := conn.ReadPacket(buff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
buff.Release()
|
buff.Release()
|
||||||
if E.IsClosed(err) {
|
if ShouldIgnorePacketError(err) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -127,6 +142,14 @@ func (h *ListenerHandler) NewError(ctx context.Context, err error) {
|
|||||||
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
|
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ShouldIgnorePacketError(err error) bool {
|
||||||
|
// ignore simple error
|
||||||
|
if E.IsTimeout(err) || E.IsClosed(err) || E.IsCanceled(err) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type packet struct {
|
type packet struct {
|
||||||
conn *network.PacketConn
|
conn *network.PacketConn
|
||||||
mutex *sync.Mutex
|
mutex *sync.Mutex
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
D "github.com/miekg/dns"
|
D "github.com/miekg/dns"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
"github.com/sagernet/sing/common/network"
|
"github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
@ -110,11 +109,14 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
|||||||
conn2 = nil
|
conn2 = nil
|
||||||
}()
|
}()
|
||||||
for {
|
for {
|
||||||
buff := buf.NewPacket()
|
// safe size which is 1232 from https://dnsflagday.net/2020/.
|
||||||
|
// so 2048 is enough
|
||||||
|
buff := buf.NewSize(2 * 1024)
|
||||||
|
_ = conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout))
|
||||||
dest, err := conn.ReadPacket(buff)
|
dest, err := conn.ReadPacket(buff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
buff.Release()
|
buff.Release()
|
||||||
if E.IsClosed(err) {
|
if sing.ShouldIgnorePacketError(err) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -229,8 +229,8 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
|
|||||||
err = E.Cause(err, "configure tun interface")
|
err = E.Cause(err, "configure tun interface")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.tunIf = tunIf
|
|
||||||
l.tunStack, err = tun.NewStack(strings.ToLower(options.Stack.String()), tun.StackOptions{
|
stackOptions := tun.StackOptions{
|
||||||
Context: context.TODO(),
|
Context: context.TODO(),
|
||||||
Tun: tunIf,
|
Tun: tunIf,
|
||||||
MTU: tunOptions.MTU,
|
MTU: tunOptions.MTU,
|
||||||
@ -241,7 +241,16 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
|
|||||||
UDPTimeout: udpTimeout,
|
UDPTimeout: udpTimeout,
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
Logger: log.SingLogger,
|
Logger: log.SingLogger,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if options.FileDescriptor > 0 {
|
||||||
|
if tunName, err := getTunnelName(int32(options.FileDescriptor)); err != nil {
|
||||||
|
stackOptions.Name = tunName
|
||||||
|
stackOptions.ForwarderBindInterface = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.tunIf = tunIf
|
||||||
|
l.tunStack, err = tun.NewStack(strings.ToLower(options.Stack.String()), stackOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
11
listener/sing_tun/tun_name_darwin.go
Normal file
11
listener/sing_tun/tun_name_darwin.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package sing_tun
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
func getTunnelName(fd int32) (string, error) {
|
||||||
|
return unix.GetsockoptString(
|
||||||
|
int(fd),
|
||||||
|
2, /* #define SYSPROTO_CONTROL 2 */
|
||||||
|
2, /* #define UTUN_OPT_IFNAME 2 */
|
||||||
|
)
|
||||||
|
}
|
25
listener/sing_tun/tun_name_linux.go
Normal file
25
listener/sing_tun/tun_name_linux.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package sing_tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ifReqSize = unix.IFNAMSIZ + 64
|
||||||
|
|
||||||
|
func getTunnelName(fd int32) (string, error) {
|
||||||
|
var ifr [ifReqSize]byte
|
||||||
|
var errno syscall.Errno
|
||||||
|
_, _, errno = unix.Syscall(
|
||||||
|
unix.SYS_IOCTL,
|
||||||
|
uintptr(fd),
|
||||||
|
uintptr(unix.TUNGETIFF),
|
||||||
|
uintptr(unsafe.Pointer(&ifr[0])),
|
||||||
|
)
|
||||||
|
if errno != 0 {
|
||||||
|
return "", fmt.Errorf("failed to get name of TUN device: %w", errno)
|
||||||
|
}
|
||||||
|
return unix.ByteSliceToString(ifr[:]), nil
|
||||||
|
}
|
9
listener/sing_tun/tun_name_other.go
Normal file
9
listener/sing_tun/tun_name_other.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
//go:build !(darwin || linux)
|
||||||
|
|
||||||
|
package sing_tun
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func getTunnelName(fd int32) (string, error) {
|
||||||
|
return "", os.ErrInvalid
|
||||||
|
}
|
@ -1,53 +0,0 @@
|
|||||||
Subject: [PATCH] Chore: add debug api
|
|
||||||
---
|
|
||||||
Index: hub/route/debug.go
|
|
||||||
IDEA additional info:
|
|
||||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
||||||
<+>UTF-8
|
|
||||||
===================================================================
|
|
||||||
diff --git a/hub/route/debug.go b/hub/route/debug.go
|
|
||||||
new file mode 100644
|
|
||||||
--- /dev/null (revision df1007e2b14f7a526d176410995998bf06054657)
|
|
||||||
+++ b/hub/route/debug.go (revision df1007e2b14f7a526d176410995998bf06054657)
|
|
||||||
@@ -0,0 +1,21 @@
|
|
||||||
+package route
|
|
||||||
+
|
|
||||||
+import (
|
|
||||||
+ "github.com/Dreamacro/clash/log"
|
|
||||||
+ "github.com/go-chi/chi/v5"
|
|
||||||
+ "github.com/go-chi/chi/v5/middleware"
|
|
||||||
+ "net/http"
|
|
||||||
+ "runtime"
|
|
||||||
+)
|
|
||||||
+
|
|
||||||
+func debugRouter() http.Handler {
|
|
||||||
+ handler := middleware.Profiler()
|
|
||||||
+ r := chi.NewRouter()
|
|
||||||
+ r.Mount("/", handler)
|
|
||||||
+ r.Put("/gc", func(writer http.ResponseWriter, request *http.Request) {
|
|
||||||
+ log.Debugln("trigger GC")
|
|
||||||
+ runtime.GC()
|
|
||||||
+ })
|
|
||||||
+
|
|
||||||
+ return r
|
|
||||||
+}
|
|
||||||
Index: hub/route/server.go
|
|
||||||
IDEA additional info:
|
|
||||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
||||||
<+>UTF-8
|
|
||||||
===================================================================
|
|
||||||
diff --git a/hub/route/server.go b/hub/route/server.go
|
|
||||||
--- a/hub/route/server.go (revision f83fd6c690928ca7861196e3ca5af566303f95d5)
|
|
||||||
+++ b/hub/route/server.go (revision df1007e2b14f7a526d176410995998bf06054657)
|
|
||||||
@@ -59,6 +59,11 @@
|
|
||||||
MaxAge: 300,
|
|
||||||
})
|
|
||||||
r.Use(corsM.Handler)
|
|
||||||
+
|
|
||||||
+ r.Group(func(r chi.Router) {
|
|
||||||
+ r.Mount("/debug", debugRouter())
|
|
||||||
+ })
|
|
||||||
+
|
|
||||||
r.Group(func(r chi.Router) {
|
|
||||||
r.Use(authentication)
|
|
||||||
r.Get("/", hello)
|
|
@ -1,7 +1,6 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/net/idna"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -11,7 +10,6 @@ type Domain struct {
|
|||||||
*Base
|
*Base
|
||||||
domain string
|
domain string
|
||||||
adapter string
|
adapter string
|
||||||
isIDNA bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Domain) RuleType() C.RuleType {
|
func (d *Domain) RuleType() C.RuleType {
|
||||||
@ -27,20 +25,14 @@ func (d *Domain) Adapter() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Domain) Payload() string {
|
func (d *Domain) Payload() string {
|
||||||
domain := d.domain
|
return d.domain
|
||||||
if d.isIDNA {
|
|
||||||
domain, _ = idna.ToUnicode(domain)
|
|
||||||
}
|
|
||||||
return domain
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomain(domain string, adapter string) *Domain {
|
func NewDomain(domain string, adapter string) *Domain {
|
||||||
actualDomain, _ := idna.ToASCII(domain)
|
|
||||||
return &Domain{
|
return &Domain{
|
||||||
Base: &Base{},
|
Base: &Base{},
|
||||||
domain: strings.ToLower(actualDomain),
|
domain: strings.ToLower(domain),
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
isIDNA: actualDomain != domain,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/net/idna"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
@ -11,7 +10,6 @@ type DomainKeyword struct {
|
|||||||
*Base
|
*Base
|
||||||
keyword string
|
keyword string
|
||||||
adapter string
|
adapter string
|
||||||
isIDNA bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dk *DomainKeyword) RuleType() C.RuleType {
|
func (dk *DomainKeyword) RuleType() C.RuleType {
|
||||||
@ -28,20 +26,14 @@ func (dk *DomainKeyword) Adapter() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dk *DomainKeyword) Payload() string {
|
func (dk *DomainKeyword) Payload() string {
|
||||||
keyword := dk.keyword
|
return dk.keyword
|
||||||
if dk.isIDNA {
|
|
||||||
keyword, _ = idna.ToUnicode(keyword)
|
|
||||||
}
|
|
||||||
return keyword
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomainKeyword(keyword string, adapter string) *DomainKeyword {
|
func NewDomainKeyword(keyword string, adapter string) *DomainKeyword {
|
||||||
actualDomainKeyword, _ := idna.ToASCII(keyword)
|
|
||||||
return &DomainKeyword{
|
return &DomainKeyword{
|
||||||
Base: &Base{},
|
Base: &Base{},
|
||||||
keyword: strings.ToLower(actualDomainKeyword),
|
keyword: strings.ToLower(keyword),
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
isIDNA: keyword != actualDomainKeyword,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user