Compare commits

..

30 Commits

Author SHA1 Message Date
d4e48d709f chore: add test case 2023-04-01 11:21:38 +08:00
f54bf2d935 fix: multiple wildcard 2023-04-01 11:17:15 +08:00
971131d951 chore: add comment 2023-04-01 10:52:20 +08:00
07fb529e36 fix: processing data 2023-04-01 10:38:30 +08:00
a6ee3348df chore: optimized initialization 2023-03-31 23:51:09 +08:00
53b1250cd8 chore: print low_memory tag 2023-03-31 21:03:42 +08:00
3a4b5fb145 chore: remove unused field 2023-03-31 20:57:26 +08:00
858d9de250 chore: default buffer use standard 2023-03-31 20:50:43 +08:00
1e989b68bd fix: wildcard match 2023-03-31 20:43:49 +08:00
752074c68e Merge pull request #475 from rookisbusy/memory
adjust buff size,same as sing
2023-03-30 16:45:28 +08:00
d0663277dc safe dns buff size 2023-03-30 14:17:25 +08:00
14a0a3c790 adjust buff size,same as sing 2023-03-30 14:05:05 +08:00
6c9c0bd755 chore: add unit test and adjust logic error 2023-03-30 13:36:14 +08:00
e0cf342672 chore: clean code 2023-03-29 22:55:36 +08:00
7b7a8981a6 chore: trigger gc when the rule is loaded 2023-03-29 22:46:16 +08:00
d462257e53 chore: force memory to be released once after rule loading 2023-03-29 22:39:07 +08:00
2e1c5091c3 chore: do not load cert pool if not necessary 2023-03-29 20:12:12 +08:00
ae13418929 fix: broken url 2023-03-29 19:43:06 +08:00
ae8913587d fix: better DomainSet 2023-03-29 18:47:56 +08:00
c486d7c67e fix: filtering out duplicate domains 2023-03-29 17:40:22 +08:00
4c4f734572 fix: adjust log level 2023-03-29 16:29:37 +08:00
82dddf932a chore: add classical provider for nameserver-policy, and rename domain-set to rule-set 2023-03-29 16:28:26 +08:00
75ed879121 chore: update docs/config.yaml 2023-03-29 14:34:29 +08:00
5b73942960 chore: reuse cert pool 2023-03-29 14:09:42 +08:00
d305e0ddfc chore: with_gvisor output 2023-03-29 13:58:37 +08:00
9f7a0052a8 fix: an empty set match will panic 2023-03-29 13:43:10 +08:00
c6873c5c04 chore: add no_fake_tcp complie option 2023-03-29 13:35:29 +08:00
c3ed06e99d chore: sniffer use TrieSet 2023-03-29 13:24:26 +08:00
d52748165f feat: nameserver-policy support domain-set, reduce domain-set memory usage 2023-03-29 13:14:02 +08:00
56e525114d chore: use inner for upgrade core 2023-03-28 17:37:00 +00:00
121 changed files with 5542 additions and 2709 deletions

View File

@ -1,82 +0,0 @@
name: Bug report
description: Create a report to help us improve
title: "[Bug] "
body:
- type: checkboxes
id: ensure
attributes:
label: Verify steps
description: "
在提交之前,请确认
Please verify that you've followed these steps
"
options:
- label: "
确保你使用的是**本仓库**最新的的 clash 或 clash Alpha 版本
Ensure you are using the latest version of Clash or Clash Premium from **this repository**.
"
required: true
- label: "
如果你可以自己 debug 并解决的话,提交 PR 吧
Is this something you can **debug and fix**? Send a pull request! Bug fixes and documentation fixes are welcome.
"
required: false
- label: "
我已经在 [Issue Tracker](……/) 中找过我要提出的问题
I have searched on the [issue tracker](……/) for a related issue.
"
required: true
- label: "
我已经使用 Alpha 分支版本测试过,问题依旧存在
I have tested using the dev branch, and the issue still exists.
"
required: true
- label: "
我已经仔细看过 [Documentation](https://wiki.metacubex.one/) 并无法自行解决问题
I have read the [documentation](https://wiki.metacubex.one/) and was unable to solve the issue.
"
required: true
- label: "
这是 Clash 核心的问题,并非我所使用的 Clash 衍生版本(如 OpenClash、KoolClash 等)的特定问题
This is an issue of the Clash core *per se*, not to the derivatives of Clash, like OpenClash or KoolClash.
"
required: true
- type: input
attributes:
label: Clash version
description: "use `clash -v`"
validations:
required: true
- type: dropdown
id: os
attributes:
label: What OS are you seeing the problem on?
multiple: true
options:
- macOS
- Windows
- Linux
- OpenBSD/FreeBSD
- type: textarea
attributes:
render: yaml
label: "Clash config"
description: "
在下方附上 Clash core 配置文件,请确保配置文件中没有敏感信息(比如:服务器地址,密码,端口等)
Paste the Clash core configuration file below, please make sure that there is no sensitive information in the configuration file (e.g., server address/url, password, port)
"
validations:
required: true
- type: textarea
attributes:
render: shell
label: Clash log
description: "
在下方附上 Clash Core 的日志log level 使用 DEBUG
Paste the Clash core log below with the log level set to `DEBUG`.
"
- type: textarea
attributes:
label: Description
validations:
required: true

View File

@ -1,36 +0,0 @@
name: Feature request
description: Suggest an idea for this project
title: "[Feature] "
body:
- type: checkboxes
id: ensure
attributes:
label: Verify steps
description: "
在提交之前,请确认
Please verify that you've followed these steps
"
options:
- label: "
我已经在 [Issue Tracker](……/) 中找过我要提出的请求
I have searched on the [issue tracker](……/) for a related feature request.
"
required: true
- label: "
我已经仔细看过 [Documentation](https://wiki.metacubex.one/) 并无法找到这个功能
I have read the [documentation](https://wiki.metacubex.one/) and was unable to solve the issue.
"
required: true
- type: textarea
attributes:
label: Description
description: 请详细、清晰地表达你要提出的论述,例如这个问题如何影响到你?你想实现什么功能?目前 Clash Core 的行为是什麽?
validations:
required: true
- type: textarea
attributes:
label: Possible Solution
description: "
此项非必须,但是如果你有想法的话欢迎提出。
Not obligatory, but suggest a fix/reason for the bug, or ideas how to implement the addition or change
"

View File

@ -1 +0,0 @@
git log --pretty=format:"* %s by @%an" v1.14.x..v1.14.y | sort -f | uniq > release.md

View File

@ -5,14 +5,17 @@ on:
paths-ignore:
- "docs/**"
- "README.md"
- ".github/ISSUE_TEMPLATE/**"
branches:
- Alpha
- Beta
- Meta
tags:
- "v*"
pull_request_target:
branches:
- Alpha
- Beta
- Meta
concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
@ -265,6 +268,18 @@ jobs:
generate_release_notes: true
body_path: release.txt
- name: Git push assets to "release" branch
run: |
cd bin || exit 1
git init
git config --local user.name "github-actions[bot]"
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b release
git add .
git commit -m "${{ env.BUILDTIME }}"
git remote add origin "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}"
git push -f -u origin release
Upload-Release:
permissions: write-all
if: ${{ github.ref_type=='tag' }}

View File

@ -1,15 +0,0 @@
name: Delete old workflow runs
on:
schedule:
- cron: "0 0 * * SUN"
jobs:
del_runs:
runs-on: ubuntu-latest
steps:
- name: Delete workflow runs
uses: GitRML/delete-workflow-runs@main
with:
token: ${{ secrets.AUTH_PAT }}
repository: ${{ github.repository }}
retain_days: 30

View File

@ -30,7 +30,8 @@
- Comprehensive HTTP RESTful API controller
## Wiki
Configuration examples can be found at [/docs/config.yaml](https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml), while documentation can be found [Clash.Meta Wiki](https://clash-meta.wiki).
Documentation and configuring examples are available on [Clash.Meta Wiki](https://clash-meta.wiki).
## Build

View File

@ -4,16 +4,16 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/Dreamacro/clash/common/queue"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"net"
"net/http"
"net/netip"
"net/url"
"time"
"github.com/Dreamacro/clash/common/atomic"
"github.com/Dreamacro/clash/common/queue"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"go.uber.org/atomic"
)
var UnifiedDelay = atomic.NewBool(false)

View File

@ -42,8 +42,6 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext {
if host == "" {
if ip, err := netip.ParseAddr(h); err == nil {
metadata.DstIP = ip
} else {
metadata.Host = h
}
}
}

View File

@ -3,9 +3,9 @@ package outbound
import (
"context"
"encoding/json"
"errors"
"net"
"strings"
"syscall"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/utils"
@ -47,31 +47,31 @@ func (b *Base) Type() C.AdapterType {
// StreamConn implements C.ProxyAdapter
func (b *Base) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
return c, C.ErrNotSupport
return c, errors.New("no support")
}
func (b *Base) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
return nil, C.ErrNotSupport
return nil, errors.New("no support")
}
// DialContextWithDialer implements C.ProxyAdapter
func (b *Base) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
return nil, C.ErrNotSupport
return nil, errors.New("no support")
}
// ListenPacketContext implements C.ProxyAdapter
func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
return nil, C.ErrNotSupport
return nil, errors.New("no support")
}
// ListenPacketWithDialer implements C.ProxyAdapter
func (b *Base) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
return nil, C.ErrNotSupport
return nil, errors.New("no support")
}
// SupportWithDialer implements C.ProxyAdapter
func (b *Base) SupportWithDialer() C.NetWork {
return C.InvalidNet
func (b *Base) SupportWithDialer() bool {
return false
}
// SupportUOT implements C.ProxyAdapter
@ -94,11 +94,6 @@ func (b *Base) SupportTFO() bool {
return b.tfo
}
// IsL3Protocol implements C.ProxyAdapter
func (b *Base) IsL3Protocol(metadata *C.Metadata) bool {
return false
}
// MarshalJSON implements C.ProxyAdapter
func (b *Base) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]string{
@ -151,7 +146,6 @@ type BasicOption struct {
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,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 {
@ -204,18 +198,7 @@ func (c *conn) Upstream() any {
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 {
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())}
}
@ -247,9 +230,6 @@ func (c *packetConn) LocalAddr() net.Addr {
}
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())}
}

View File

@ -2,7 +2,6 @@ package outbound
import (
"context"
"errors"
"net"
"github.com/Dreamacro/clash/component/dialer"
@ -27,14 +26,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
// ListenPacketContext implements C.ProxyAdapter
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
// 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
}
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", metadata.DstIP), "", d.Base.DialOptions(opts...)...)
if err != nil {
return nil, err

View File

@ -14,7 +14,6 @@ import (
"strconv"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
)
@ -67,12 +66,6 @@ func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di
// DialContextWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", h.addr, err)
@ -92,8 +85,8 @@ func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metad
}
// SupportWithDialer implements C.ProxyAdapter
func (h *Http) SupportWithDialer() C.NetWork {
return C.TCP
func (h *Http) SupportWithDialer() bool {
return true
}
func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {

View File

@ -20,7 +20,6 @@ import (
M "github.com/sagernet/sing/common/metadata"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
@ -29,7 +28,6 @@ import (
"github.com/Dreamacro/clash/transport/hysteria/obfs"
"github.com/Dreamacro/clash/transport/hysteria/pmtud_fix"
"github.com/Dreamacro/clash/transport/hysteria/transport"
"github.com/Dreamacro/clash/transport/hysteria/utils"
)
const (
@ -48,12 +46,21 @@ var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`)
type Hysteria struct {
*Base
option *HysteriaOption
client *core.Client
}
func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), h.genHdc(ctx, opts...))
hdc := hyDialerWithContext{
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 {
return nil, err
}
@ -62,32 +69,20 @@ 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) {
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{
hdc := hyDialerWithContext{
ctx: context.Background(),
hyDialer: func(network string) (net.PacketConn, error) {
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)
return dialer.ListenPacket(ctx, network, "", h.Base.DialOptions(opts...)...)
},
remoteAddr: func(addr string) (net.Addr, error) {
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 {
@ -263,7 +258,6 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
option: &option,
client: client,
}, nil
}

View File

@ -11,8 +11,6 @@ import (
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
"github.com/Dreamacro/clash/component/resolver"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/restls"
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
@ -147,12 +145,6 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, op
// DialContextWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
@ -174,18 +166,17 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta
// ListenPacketWithDialer implements C.ProxyAdapter
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 {
tcpConn, err := ss.DialContextWithDialer(ctx, dialer, metadata)
if err != nil {
return nil, err
}
return ss.ListenPacketOnStreamConn(ctx, tcpConn, metadata)
destination := M.ParseSocksaddr(metadata.RemoteAddress())
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)
if err != nil {
@ -196,35 +187,26 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial
if err != nil {
return nil, err
}
pc = ss.method.DialPacketConn(bufio.NewBindPacketConn(pc, addr))
pc = ss.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: pc, Addr: addr})
return newPacketConn(pc, ss), nil
}
// SupportWithDialer implements C.ProxyAdapter
func (ss *ShadowSocks) SupportWithDialer() C.NetWork {
return C.ALLNet
func (ss *ShadowSocks) SupportWithDialer() bool {
return true
}
// ListenPacketOnStreamConn implements C.ProxyAdapter
func (ss *ShadowSocks) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
if ss.option.UDPOverTCP {
// 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())
destination := M.ParseSocksaddr(metadata.RemoteAddress())
if ss.option.UDPOverTCPVersion == uot.LegacyVersion {
return newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), ss), nil
} else {
return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), ss), nil
}
}
return nil, C.ErrNotSupport
return nil, errors.New("no support")
}
// SupportUOT implements C.ProxyAdapter

View File

@ -7,7 +7,6 @@ import (
"strconv"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/shadowsocks/core"
"github.com/Dreamacro/clash/transport/shadowsocks/shadowaead"
@ -18,7 +17,6 @@ import (
type ShadowSocksR struct {
*Base
option *ShadowSocksROption
cipher core.Cipher
obfs obfs.Obfs
protocol protocol.Protocol
@ -67,12 +65,6 @@ func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata,
// DialContextWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err)
@ -94,12 +86,6 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me
// ListenPacketWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, err
@ -116,8 +102,8 @@ func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Di
}
// SupportWithDialer implements C.ProxyAdapter
func (ssr *ShadowSocksR) SupportWithDialer() C.NetWork {
return C.ALLNet
func (ssr *ShadowSocksR) SupportWithDialer() bool {
return true
}
func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
@ -182,7 +168,6 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
option: &option,
cipher: coreCiph,
obfs: obfs,
protocol: protocol,

View File

@ -1,138 +0,0 @@
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
}

View File

@ -8,7 +8,6 @@ import (
"github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
C "github.com/Dreamacro/clash/constant"
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
"github.com/Dreamacro/clash/transport/snell"
@ -16,7 +15,6 @@ import (
type Snell struct {
*Base
option *SnellOption
psk []byte
pool *snell.Pool
obfsOption *simpleObfsOption
@ -85,12 +83,6 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
// DialContextWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", s.addr, err)
@ -112,13 +104,6 @@ func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
// ListenPacketWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, err
@ -136,8 +121,8 @@ func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
}
// SupportWithDialer implements C.ProxyAdapter
func (s *Snell) SupportWithDialer() C.NetWork {
return C.ALLNet
func (s *Snell) SupportWithDialer() bool {
return true
}
// SupportUOT implements C.ProxyAdapter
@ -187,7 +172,6 @@ func NewSnell(option SnellOption) (*Snell, error) {
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
option: &option,
psk: psk,
obfsOption: obfsOption,
version: option.Version,
@ -195,15 +179,7 @@ func NewSnell(option SnellOption) (*Snell, error) {
if option.Version == snell.Version2 {
s.pool = snell.NewPool(func(ctx context.Context) (*snell.Snell, error) {
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)
c, err := dialer.DialContext(ctx, "tcp", addr, s.Base.DialOptions()...)
if err != nil {
return nil, err
}

View File

@ -10,7 +10,6 @@ import (
"strconv"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5"
@ -18,7 +17,6 @@ import (
type Socks5 struct {
*Base
option *Socks5Option
user string
pass string
tls bool
@ -72,12 +70,6 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
// DialContextWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
@ -97,20 +89,13 @@ func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, me
}
// SupportWithDialer implements C.ProxyAdapter
func (ss *Socks5) SupportWithDialer() C.NetWork {
return C.TCP
func (ss *Socks5) SupportWithDialer() bool {
return true
}
// ListenPacketContext implements C.ProxyAdapter
func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
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)
c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...)
if err != nil {
err = fmt.Errorf("%s connect error: %w", ss.addr, err)
return
@ -202,7 +187,6 @@ func NewSocks5(option Socks5Option) (*Socks5, error) {
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
option: &option,
user: option.UserName,
pass: option.Password,
tls: option.TLS,

View File

@ -8,8 +8,8 @@ import (
"net/http"
"strconv"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
"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
}
err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata))
return c, err
return N.NewExtendedConn(c), err
}
// DialContext implements C.ProxyAdapter
@ -135,12 +135,6 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
// DialContextWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
@ -185,12 +179,6 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
// ListenPacketWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
@ -214,8 +202,8 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me
}
// SupportWithDialer implements C.ProxyAdapter
func (t *Trojan) SupportWithDialer() C.NetWork {
return C.ALLNet
func (t *Trojan) SupportWithDialer() bool {
return true
}
// ListenPacketOnStreamConn implements C.ProxyAdapter
@ -283,15 +271,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
if option.Network == "grpc" {
dialFn := func(network, addr string) (net.Conn, error) {
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)
c, err := dialer.DialContext(context.Background(), "tcp", t.addr, t.Base.DialOptions()...)
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error())
}

View File

@ -16,7 +16,6 @@ import (
"github.com/metacubex/quic-go"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/tuic"
@ -24,7 +23,6 @@ import (
type Tuic struct {
*Base
option *TuicOption
client *tuic.PoolClient
}
@ -86,8 +84,8 @@ func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, meta
}
// SupportWithDialer implements C.ProxyAdapter
func (t *Tuic) SupportWithDialer() C.NetWork {
return C.ALLNet
func (t *Tuic) SupportWithDialer() bool {
return true
}
func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) {
@ -95,12 +93,6 @@ 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) {
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)
if err != nil {
return nil, nil, err
@ -238,7 +230,6 @@ func NewTuic(option TuicOption) (*Tuic, error) {
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
option: &option,
}
clientMaxOpenStreams := int64(option.MaxOpenStreams)

View File

@ -14,7 +14,6 @@ import (
"github.com/Dreamacro/clash/common/convert"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
"github.com/Dreamacro/clash/component/resolver"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
@ -169,35 +168,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
return nil, err
}
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
return v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP))
}
func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) {
@ -267,12 +238,6 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
// DialContextWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
@ -299,6 +264,7 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
}
metadata.DstIP = ip
}
var c net.Conn
// gun transport
if v.transport != nil && len(opts) == 0 {
@ -310,25 +276,27 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
safeConnClose(c, err)
}(c)
c, err = v.streamConn(c, metadata)
if v.option.PacketAddr {
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 {
return nil, fmt.Errorf("new vless client error: %v", err)
}
return v.ListenPacketOnStreamConn(ctx, c, metadata)
return v.ListenPacketOnStreamConn(c, metadata)
}
return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata)
}
// ListenPacketWithDialer implements C.ProxyAdapter
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
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(ctx, metadata.Host)
@ -337,7 +305,6 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
}
metadata.DstIP = ip
}
c, err := dialer.DialContext(ctx, "tcp", v.addr)
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
@ -347,39 +314,39 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
safeConnClose(c, err)
}(c)
c, err = v.StreamConn(c, metadata)
if v.option.PacketAddr {
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 {
return nil, fmt.Errorf("new vless client error: %v", err)
}
return v.ListenPacketOnStreamConn(ctx, c, metadata)
return v.ListenPacketOnStreamConn(c, metadata)
}
// SupportWithDialer implements C.ProxyAdapter
func (v *Vless) SupportWithDialer() C.NetWork {
return C.ALLNet
func (v *Vless) SupportWithDialer() bool {
return true
}
// ListenPacketOnStreamConn implements C.ProxyAdapter
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
}
func (v *Vless) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
if v.option.XUDP {
return newPacketConn(&threadSafePacketConn{
PacketConn: vmessSing.NewXUDPConn(c, M.SocksaddrFromNet(metadata.UDPAddr())),
PacketConn: vmessSing.NewXUDPConn(c, M.ParseSocksaddr(metadata.RemoteAddress())),
}, v), nil
} else if v.option.PacketAddr {
return newPacketConn(&threadSafePacketConn{
PacketConn: packetaddr.NewConn(&vlessPacketConn{
Conn: c, rAddr: metadata.UDPAddr(),
}, M.SocksaddrFromNet(metadata.UDPAddr())),
}, M.ParseSocksaddr(metadata.RemoteAddress())),
}, v), nil
}
return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
@ -537,9 +504,6 @@ func NewVless(option VlessOption) (*Vless, error) {
option.XUDP = true
}
}
if option.XUDP {
option.PacketAddr = false
}
client, err := vless.NewClient(option.UUID, addons, option.FlowShow)
if err != nil {
@ -574,15 +538,7 @@ func NewVless(option VlessOption) (*Vless, error) {
}
case "grpc":
dialFn := func(network, addr string) (net.Conn, error) {
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)
c, err := dialer.DialContext(context.Background(), "tcp", v.addr, v.Base.DialOptions()...)
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}

View File

@ -13,7 +13,6 @@ import (
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
"github.com/Dreamacro/clash/component/resolver"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
@ -217,42 +216,27 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if err != nil {
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 v.option.XUDP {
if N.NeedHandshake(c) {
conn = v.client.DialEarlyXUDPPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
return v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
} else {
conn, err = v.client.DialXUDPPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
return v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
}
} 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 {
if N.NeedHandshake(c) {
conn = v.client.DialEarlyPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
return v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
} else {
conn, err = v.client.DialPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr()))
return v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
}
}
} else {
if N.NeedHandshake(c) {
conn = v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
return v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
} else {
conn, err = v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
return v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
}
}
if err != nil {
conn = nil
}
return
}
// DialContext implements C.ProxyAdapter
@ -279,12 +263,6 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
// DialContextWithDialer implements C.ProxyAdapter
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)
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
@ -308,6 +286,14 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
}
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
// gun transport
if v.transport != nil && len(opts) == 0 {
@ -319,24 +305,30 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
safeConnClose(c, err)
}(c)
c, err = v.streamConn(c, metadata)
if v.option.XUDP {
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 {
return nil, fmt.Errorf("new vmess client error: %v", err)
}
return v.ListenPacketOnStreamConn(ctx, c, metadata)
return v.ListenPacketOnStreamConn(c, metadata)
}
return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata)
}
// ListenPacketWithDialer implements C.ProxyAdapter
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
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(ctx, metadata.Host)
@ -359,26 +351,19 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
if err != nil {
return nil, fmt.Errorf("new vmess client error: %v", err)
}
return v.ListenPacketOnStreamConn(ctx, c, metadata)
return v.ListenPacketOnStreamConn(c, metadata)
}
// SupportWithDialer implements C.ProxyAdapter
func (v *Vmess) SupportWithDialer() C.NetWork {
return C.ALLNet
func (v *Vmess) SupportWithDialer() bool {
return true
}
// ListenPacketOnStreamConn implements C.ProxyAdapter
func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
// vmess 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 pc, ok := c.(net.PacketConn); ok {
func (v *Vmess) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
if v.option.PacketAddr {
return newPacketConn(&threadSafePacketConn{PacketConn: packetaddr.NewBindConn(c)}, v), nil
} else if pc, ok := c.(net.PacketConn); ok {
return newPacketConn(&threadSafePacketConn{PacketConn: pc}, v), nil
}
return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil
@ -443,15 +428,7 @@ func NewVmess(option VmessOption) (*Vmess, error) {
}
case "grpc":
dialFn := func(network, addr string) (net.Conn, error) {
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)
c, err := dialer.DialContext(context.Background(), "tcp", v.addr, v.Base.DialOptions()...)
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
@ -509,9 +486,9 @@ type vmessPacketConn struct {
// 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.
func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
allowedAddr := uc.rAddr
destAddr := addr
if allowedAddr.String() != destAddr.String() {
allowedAddr := uc.rAddr.(*net.UDPAddr)
destAddr := addr.(*net.UDPAddr)
if !(allowedAddr.IP.Equal(destAddr.IP) && allowedAddr.Port == destAddr.Port) {
return 0, ErrUDPRemoteAddrMismatch
}
uc.access.Lock()

View File

@ -15,10 +15,8 @@ import (
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"
"github.com/Dreamacro/clash/dns"
"github.com/Dreamacro/clash/log"
wireguard "github.com/metacubex/sing-wireguard"
@ -39,97 +37,75 @@ type WireGuard struct {
dialer *wgSingDialer
startOnce sync.Once
startErr error
resolver *dns.Resolver
refP *refProxyAdapter
}
type WireGuardOption struct {
BasicOption
WireGuardPeerOption
Name string `proxy:"name"`
PrivateKey string `proxy:"private-key"`
Workers int `proxy:"workers,omitempty"`
MTU int `proxy:"mtu,omitempty"`
UDP bool `proxy:"udp,omitempty"`
PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"`
Peers []WireGuardPeerOption `proxy:"peers,omitempty"`
RemoteDnsResolve bool `proxy:"remote-dns-resolve,omitempty"`
Dns []string `proxy:"dns,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"`
Name string `proxy:"name"`
Server string `proxy:"server"`
Port int `proxy:"port"`
Ip string `proxy:"ip,omitempty"`
Ipv6 string `proxy:"ipv6,omitempty"`
PrivateKey string `proxy:"private-key"`
PublicKey string `proxy:"public-key"`
PreSharedKey string `proxy:"pre-shared-key,omitempty"`
Reserved []uint8 `proxy:"reserved,omitempty"`
Workers int `proxy:"workers,omitempty"`
MTU int `proxy:"mtu,omitempty"`
UDP bool `proxy:"udp,omitempty"`
PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"`
}
type wgSingDialer struct {
dialer dialer.Dialer
proxyName string
dialer dialer.Dialer
}
var _ N.Dialer = (*wgSingDialer)(nil)
var _ N.Dialer = &wgSingDialer{}
func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
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())
return d.dialer.DialContext(ctx, network, destination.String())
}
func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
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))
return d.dialer.ListenPacket(ctx, "udp", "", destination.AddrPort())
}
type wgNetDialer struct {
tunDevice wireguard.Device
}
var _ dialer.NetDialer = (*wgNetDialer)(nil)
var _ dialer.NetDialer = &wgNetDialer{}
func (d wgNetDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
return d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address).Unwrap())
}
func (option WireGuardPeerOption) Addr() M.Socksaddr {
return M.ParseSocksaddrHostPort(option.Server, uint16(option.Port))
}
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()},
}
runtime.SetFinalizer(outbound, closeWireGuard)
func (option WireGuardPeerOption) Prefixes() ([]netip.Prefix, error) {
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))
}
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)
if len(option.Ip) > 0 {
if !strings.Contains(option.Ip, "/") {
@ -154,46 +130,7 @@ func (option WireGuardPeerOption) Prefixes() ([]netip.Prefix, error) {
if len(localPrefixes) == 0 {
return nil, E.New("missing local address")
}
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
var privateKey, peerPublicKey, preSharedKey string
{
bytes, err := base64.StdEncoding.DecodeString(option.PrivateKey)
if err != nil {
@ -201,92 +138,40 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
}
privateKey = hex.EncodeToString(bytes)
}
ipcConf := "private_key=" + privateKey
if peersLen := len(option.Peers); peersLen > 0 {
localPrefixes = make([]netip.Prefix, 0, peersLen*2)
for i, peer := range option.Peers {
var peerPublicKey, preSharedKey string
{
bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey)
if err != nil {
return nil, E.Cause(err, "decode public key for peer ", i)
}
peerPublicKey = hex.EncodeToString(bytes)
}
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()
{
bytes, err := base64.StdEncoding.DecodeString(option.PublicKey)
if err != nil {
return nil, err
return nil, E.Cause(err, "decode peer public key")
}
var has4, has6 bool
for _, address := range localPrefixes {
if address.Addr().Is4() {
has4 = true
} else {
has6 = true
}
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")
}
if has4 {
ipcConf += "\nallowed_ip=0.0.0.0/0"
}
if has6 {
ipcConf += "\nallowed_ip=::/0"
preSharedKey = hex.EncodeToString(bytes)
}
ipcConf := "private_key=" + privateKey
ipcConf += "\npublic_key=" + peerPublicKey
ipcConf += "\nendpoint=" + peerAddr.String()
if preSharedKey != "" {
ipcConf += "\npreshared_key=" + preSharedKey
}
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 option.PersistentKeepalive != 0 {
ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive)
}
@ -294,9 +179,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
if mtu == 0 {
mtu = 1408
}
if len(localPrefixes) == 0 {
return nil, E.New("missing local address")
}
var err error
outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu))
if err != nil {
@ -304,45 +186,20 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
}
outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{
Verbosef: func(format string, args ...interface{}) {
log.SingLogger.Debug(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...)))
log.SingLogger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
},
Errorf: func(format string, args ...interface{}) {
log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...)))
log.SingLogger.Error(fmt.Sprintf(strings.ToLower(format), args...))
},
}, option.Workers)
if debug.Enabled {
log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", option.Name, ipcConf))
log.SingLogger.Trace("created wireguard ipc conf: \n", ipcConf)
}
err = outbound.device.IpcSet(ipcConf)
if err != nil {
return nil, E.Cause(err, "setup wireguard")
}
//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
}
@ -363,14 +220,8 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts
if w.startErr != nil {
return nil, w.startErr
}
if !metadata.Resolved() || w.resolver != nil {
r := resolver.DefaultResolver
if w.resolver != nil {
w.refP.SetProxyAdapter(w)
defer w.refP.ClearProxyAdapter()
r = w.resolver
}
options = append(options, dialer.WithResolver(r))
if !metadata.Resolved() {
options = append(options, dialer.WithResolver(resolver.DefaultResolver))
options = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: w.tunDevice}))
conn, err = dialer.NewDialer(options...).DialContext(ctx, "tcp", metadata.RemoteAddress())
} else {
@ -399,14 +250,8 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat
if err != nil {
return nil, err
}
if (!metadata.Resolved() || w.resolver != nil) && 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 !metadata.Resolved() {
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
@ -422,144 +267,3 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat
}
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)

View File

@ -37,13 +37,16 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
}
if N.NeedHandshake(c) {
c = callback.NewFirstWriteCallBackConn(c, func(err error) {
if err == nil {
f.onDialSuccess()
} else {
f.onDialFailed(proxy.Type(), err)
}
})
c = &callback.FirstWriteCallBackConn{
Conn: c,
Callback: func(err error) {
if err == nil {
f.onDialSuccess()
} else {
f.onDialFailed(proxy.Type(), err)
}
},
}
}
return c, err
@ -70,11 +73,6 @@ func (f *Fallback) SupportUDP() bool {
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
func (f *Fallback) MarshalJSON() ([]byte, error) {
all := []string{}
@ -138,10 +136,6 @@ func (f *Fallback) Set(name string) error {
return nil
}
func (f *Fallback) ForceSet(name string) {
f.selected = name
}
func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
return &Fallback{
GroupBase: NewGroupBase(GroupBaseOption{

View File

@ -8,7 +8,6 @@ import (
"time"
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/common/atomic"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
types "github.com/Dreamacro/clash/constant/provider"
@ -16,6 +15,7 @@ import (
"github.com/Dreamacro/clash/tunnel"
"github.com/dlclark/regexp2"
"go.uber.org/atomic"
)
type GroupBase struct {

View File

@ -95,13 +95,16 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op
}
if N.NeedHandshake(c) {
c = callback.NewFirstWriteCallBackConn(c, func(err error) {
if err == nil {
lb.onDialSuccess()
} else {
lb.onDialFailed(proxy.Type(), err)
}
})
c = &callback.FirstWriteCallBackConn{
Conn: c,
Callback: func(err error) {
if err == nil {
lb.onDialSuccess()
} else {
lb.onDialFailed(proxy.Type(), err)
}
},
}
}
return
@ -124,11 +127,6 @@ func (lb *LoadBalance) SupportUDP() bool {
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 {
idx := 0
idxMutex := sync.Mutex{}

View File

@ -3,9 +3,13 @@ package outboundgroup
import (
"context"
"encoding/json"
"net"
"net/netip"
"strings"
"github.com/Dreamacro/clash/adapter/outbound"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/proxydialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
)
@ -14,6 +18,36 @@ type Relay struct {
*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
func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
proxies, chainProxies := r.proxies(metadata, true)
@ -27,7 +61,10 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
var d C.Dialer
d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
for _, proxy := range proxies[:len(proxies)-1] {
d = proxydialer.New(proxy, d, false)
d = proxyDialer{
proxy: proxy,
dialer: d,
}
}
last := proxies[len(proxies)-1]
conn, err := last.DialContextWithDialer(ctx, d, metadata)
@ -58,7 +95,10 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o
var d C.Dialer
d = dialer.NewDialer(r.Base.DialOptions(opts...)...)
for _, proxy := range proxies[:len(proxies)-1] {
d = proxydialer.New(proxy, d, false)
d = proxyDialer{
proxy: proxy,
dialer: d,
}
}
last := proxies[len(proxies)-1]
pc, err := last.ListenPacketWithDialer(ctx, d, metadata)
@ -89,10 +129,7 @@ func (r *Relay) SupportUDP() bool {
if proxy.SupportUOT() {
return true
}
switch proxy.SupportWithDialer() {
case C.ALLNet:
case C.UDP:
default: // C.TCP and C.InvalidNet
if !proxy.SupportWithDialer() {
return false
}
}

View File

@ -44,11 +44,6 @@ func (s *Selector) SupportUDP() bool {
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
func (s *Selector) MarshalJSON() ([]byte, error) {
all := []string{}
@ -78,10 +73,6 @@ func (s *Selector) Set(name string) error {
return errors.New("proxy not exist")
}
func (s *Selector) ForceSet(name string) {
s.selected = name
}
// Unwrap implements C.ProxyAdapter
func (s *Selector) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
return s.selectedProxy(touch)

View File

@ -3,7 +3,6 @@ package outboundgroup
import (
"context"
"encoding/json"
"errors"
"time"
"github.com/Dreamacro/clash/adapter/outbound"
@ -25,8 +24,6 @@ func urlTestWithTolerance(tolerance uint16) urlTestOption {
type URLTest struct {
*GroupBase
selected string
testUrl string
tolerance uint16
disableUDP bool
fastNode C.Proxy
@ -37,26 +34,6 @@ func (u *URLTest) Now() string {
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
func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (c C.Conn, err error) {
proxy := u.fast(true)
@ -68,13 +45,16 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
}
if N.NeedHandshake(c) {
c = callback.NewFirstWriteCallBackConn(c, func(err error) {
if err == nil {
u.onDialSuccess()
} else {
u.onDialFailed(proxy.Type(), err)
}
})
c = &callback.FirstWriteCallBackConn{
Conn: c,
Callback: func(err error) {
if err == nil {
u.onDialSuccess()
} else {
u.onDialFailed(proxy.Type(), err)
}
},
}
}
return c, err
@ -97,24 +77,16 @@ func (u *URLTest) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
func (u *URLTest) fast(touch bool) C.Proxy {
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
var s C.Proxy
proxies := u.GetProxies(touch)
fast := proxies[0]
if fast.Name() == u.selected {
s = fast
}
min := fast.LastDelay()
fastNotExist := true
for _, proxy := range proxies[1:] {
if u.fastNode != nil && proxy.Name() == u.fastNode.Name() {
fastNotExist = false
}
if proxy.Name() == u.selected {
s = proxy
}
if !proxy.Alive() {
continue
}
@ -125,15 +97,12 @@ func (u *URLTest) fast(touch bool) C.Proxy {
min = delay
}
}
// tolerance
if u.fastNode == nil || fastNotExist || !u.fastNode.Alive() || u.fastNode.LastDelay() > fast.LastDelay()+u.tolerance {
u.fastNode = fast
}
if s != nil {
if s.Alive() && s.LastDelay() < fast.LastDelay()+u.tolerance {
u.fastNode = s
}
}
return u.fastNode, nil
})
if shared && touch { // a shared fastSingle.Do() may cause providers untouched, so we touch them again
@ -148,12 +117,8 @@ func (u *URLTest) SupportUDP() bool {
if u.disableUDP {
return false
}
return u.fast(false).SupportUDP()
}
// IsL3Protocol implements C.ProxyAdapter
func (u *URLTest) IsL3Protocol(metadata *C.Metadata) bool {
return u.fast(false).IsL3Protocol(metadata)
return u.fast(false).SupportUDP()
}
// MarshalJSON implements C.ProxyAdapter
@ -199,7 +164,6 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o
}),
fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10),
disableUDP: option.DisableUDP,
testUrl: option.URL,
}
for _, option := range options {

View File

@ -1,10 +1,37 @@
package outboundgroup
import (
"fmt"
"net"
"net/netip"
"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) {
if tcp, ok := c.(*net.TCPConn); ok {
_ = tcp.SetKeepAlive(true)
@ -14,5 +41,4 @@ func tcpKeepAlive(c net.Conn) {
type SelectAble interface {
Set(string) error
ForceSet(name string)
}

View File

@ -114,19 +114,5 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
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
}

View File

@ -4,12 +4,13 @@ import (
"context"
"time"
"github.com/Dreamacro/clash/common/atomic"
"github.com/Dreamacro/clash/common/batch"
"github.com/Dreamacro/clash/common/singledo"
"github.com/Dreamacro/clash/common/utils"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"go.uber.org/atomic"
)
const (

View File

@ -28,7 +28,6 @@ type proxyProviderSchema struct {
Filter string `provider:"filter,omitempty"`
ExcludeFilter string `provider:"exclude-filter,omitempty"`
ExcludeType string `provider:"exclude-type,omitempty"`
DialerProxy string `provider:"dialer-proxy,omitempty"`
HealthCheck healthCheckSchema `provider:"health-check,omitempty"`
}
@ -66,7 +65,6 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
filter := schema.Filter
excludeFilter := schema.ExcludeFilter
excludeType := schema.ExcludeType
dialerProxy := schema.DialerProxy
return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, dialerProxy, vehicle, hc)
return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, vehicle, hc)
}

View File

@ -17,7 +17,6 @@ import (
C "github.com/Dreamacro/clash/constant"
types "github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel/statistic"
"github.com/dlclark/regexp2"
"gopkg.in/yaml.v3"
@ -82,7 +81,6 @@ func (pp *proxySetProvider) Initial() error {
}
pp.OnUpdate(elm)
pp.getSubscriptionInfo()
pp.closeAllConnections()
return nil
}
@ -140,24 +138,12 @@ 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) {
pd.healthCheck.close()
_ = pd.Fetcher.Destroy()
}
func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
excludeFilterReg, err := regexp2.Compile(excludeFilter, 0)
if err != nil {
return nil, fmt.Errorf("invalid excludeFilter regex: %w", err)
@ -185,7 +171,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc
healthCheck: hc,
}
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy), proxiesOnUpdate(pd))
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg), proxiesOnUpdate(pd))
pd.Fetcher = fetcher
wrapper := &ProxySetProvider{pd}
runtime.SetFinalizer(wrapper, stopProxyProvider)
@ -281,7 +267,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) {
}
}
func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp, dialerProxy string) resource.Parser[[]C.Proxy] {
func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp) resource.Parser[[]C.Proxy] {
return func(buf []byte) ([]C.Proxy, error) {
schema := &ProxySchema{}
@ -344,9 +330,6 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray
if _, ok := proxiesSet[name]; ok {
continue
}
if len(dialerProxy) > 0 {
mapping["dialer-proxy"] = dialerProxy
}
proxy, err := adapter.ParseProxy(mapping)
if err != nil {
return nil, fmt.Errorf("proxy %d error: %w", idx, err)

View File

@ -1,205 +0,0 @@
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)
}

View File

@ -1,58 +0,0 @@
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
}

View File

@ -10,11 +10,9 @@ const BufferSize = buf.BufferSize
type Buffer = buf.Buffer
var New = buf.New
var NewSize = buf.NewSize
var StackNew = buf.StackNew
var StackNewSize = buf.StackNewSize
var With = buf.With
var As = buf.As
var KeepAlive = common.KeepAlive
@ -23,7 +21,5 @@ func Dup[T any](obj T) T {
return common.Dup(obj)
}
var (
Must = common.Must
Error = common.Error
)
var Must = common.Must
var Error = common.Error

View File

@ -1,55 +1,25 @@
package callback
import (
"github.com/Dreamacro/clash/common/buf"
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
)
type firstWriteCallBackConn struct {
type FirstWriteCallBackConn struct {
C.Conn
callback func(error)
Callback func(error)
written bool
}
func (c *firstWriteCallBackConn) Write(b []byte) (n int, err error) {
func (c *FirstWriteCallBackConn) Write(b []byte) (n int, err error) {
defer func() {
if !c.written {
c.written = true
c.callback(err)
c.Callback(err)
}
}()
return c.Conn.Write(b)
}
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 {
func (c *FirstWriteCallBackConn) Upstream() any {
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,
}
}

View File

@ -5,11 +5,10 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/Dreamacro/clash/log"
"net/url"
"strconv"
"strings"
"github.com/Dreamacro/clash/log"
)
// ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config
@ -202,8 +201,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
vmess["servername"] = sni
}
network, _ := values["net"].(string)
network = strings.ToLower(network)
network := strings.ToLower(values["net"].(string))
if values["type"] == "http" {
network = "http"
} else if network == "http" {
@ -211,12 +209,9 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
}
vmess["network"] = network
tls, ok := values["tls"].(string)
if ok {
tls = strings.ToLower(tls)
if strings.HasSuffix(tls, "tls") {
vmess["tls"] = true
}
tls := strings.ToLower(values["tls"].(string))
if strings.HasSuffix(tls, "tls") {
vmess["tls"] = true
}
switch network {

View File

@ -69,16 +69,6 @@ func (c *BufferedConn) ReadBuffer(buffer *buf.Buffer) (err error) {
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 {
return c.ExtendedConn
}
@ -89,7 +79,3 @@ func (c *BufferedConn) ReaderReplaceable() bool {
}
return true
}
func (c *BufferedConn) WriterReplaceable() bool {
return true
}

View File

@ -4,12 +4,10 @@ import (
"net"
"runtime"
"time"
"github.com/Dreamacro/clash/common/buf"
)
type refConn struct {
conn ExtendedConn
conn net.Conn
ref any
}
@ -57,28 +55,8 @@ func (c *refConn) Upstream() any {
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 {
return &refConn{conn: NewExtendedConn(conn), ref: ref}
return &refConn{conn: conn, ref: ref}
}
type refPacketConn struct {

View File

@ -3,11 +3,9 @@ package net
import (
"context"
"net"
"runtime"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
"github.com/sagernet/sing/common/network"
)
@ -19,14 +17,6 @@ type ExtendedConn = network.ExtendedConn
type ExtendedWriter = network.ExtendedWriter
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 {
if earlyConn, isEarlyConn := common.Cast[network.EarlyConn](conn); isEarlyConn && earlyConn.NeedHandshake() {
return true
@ -34,11 +24,7 @@ func NeedHandshake(conn any) bool {
return false
}
type CountFunc = network.CountFunc
// Relay copies between left and right bidirectionally.
func Relay(leftConn, rightConn net.Conn) {
defer runtime.KeepAlive(leftConn)
defer runtime.KeepAlive(rightConn)
_ = bufio.CopyConn(context.TODO(), leftConn, rightConn)
}

View File

@ -5,9 +5,8 @@ import (
"testing"
"time"
"github.com/Dreamacro/clash/common/atomic"
"github.com/stretchr/testify/assert"
"go.uber.org/atomic"
)
func iterator[T any](item []T) chan T {
@ -45,7 +44,7 @@ func TestObservable_MultiSubscribe(t *testing.T) {
wg.Add(2)
waitCh := func(ch <-chan int) {
for range ch {
count.Add(1)
count.Inc()
}
wg.Done()
}

View File

@ -5,9 +5,8 @@ import (
"testing"
"time"
"github.com/Dreamacro/clash/common/atomic"
"github.com/stretchr/testify/assert"
"go.uber.org/atomic"
)
func TestBasic(t *testing.T) {
@ -27,7 +26,7 @@ func TestBasic(t *testing.T) {
go func() {
_, _, shard := single.Do(call)
if shard {
shardCount.Add(1)
shardCount.Inc()
}
wg.Done()
}()

View File

@ -1,7 +1,7 @@
package utils
import (
"github.com/gofrs/uuid/v5"
"github.com/gofrs/uuid"
"github.com/zhangyunhao116/fastrand"
)

View File

@ -1,7 +1,7 @@
package utils
import (
"github.com/gofrs/uuid/v5"
"github.com/gofrs/uuid"
"reflect"
"testing"
)

View File

@ -3,7 +3,6 @@ package dialer
import (
"context"
"encoding/binary"
"fmt"
"net"
"net/netip"
"syscall"
@ -21,19 +20,11 @@ func bind4(handle syscall.Handle, ifaceIdx int) error {
var bytes [4]byte
binary.BigEndian.PutUint32(bytes[:], uint32(ifaceIdx))
idx := *(*uint32)(unsafe.Pointer(&bytes[0]))
err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx))
if err != nil {
err = fmt.Errorf("bind4: %w", err)
}
return err
return syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx))
}
func bind6(handle syscall.Handle, ifaceIdx int) error {
err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, ifaceIdx)
if err != nil {
err = fmt.Errorf("bind6: %w", err)
}
return err
return syscall.SetsockoptInt(handle, syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, ifaceIdx)
}
func bindControl(ifaceIdx int) controlFn {
@ -58,9 +49,9 @@ func bindControl(ifaceIdx int) controlFn {
if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil {
// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
if bind4err != nil {
innerErr = fmt.Errorf("%w (%s)", bind6err, bind4err)
innerErr = bind6err
} else {
innerErr = nil
innerErr = bind4err
}
} else {
innerErr = bind6err

View File

@ -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) {
ipv4s, ipv6s := resolver.SortationAddr(ips)
ipv4s, ipv6s := sortationAddr(ips)
preferIPVersion := opt.prefer
fallbackTicker := time.NewTicker(fallbackTimeout)
@ -309,16 +309,27 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso
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 {
Opt option
opt option
}
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) {
opt := WithOption(d.Opt)
opt := WithOption(d.opt)
if rAddrPort.Addr().Unmap().IsLoopback() {
// avoid "The requested address is not valid in its context."
opt = WithInterface("")
@ -328,5 +339,5 @@ func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddr
func NewDialer(options ...Option) Dialer {
opt := applyOptions(options...)
return Dialer{Opt: *opt}
return Dialer{opt: *opt}
}

View File

@ -4,13 +4,14 @@ import (
"context"
"net"
"github.com/Dreamacro/clash/common/atomic"
"github.com/Dreamacro/clash/component/resolver"
"go.uber.org/atomic"
)
var (
DefaultOptions []Option
DefaultInterface = atomic.NewTypedValue[string]("")
DefaultInterface = atomic.NewString("")
DefaultRoutingMark = atomic.NewInt32(0)
)

View File

@ -105,22 +105,10 @@ func (c *tfoConn) Upstream() any {
return c.Conn
}
func (c *tfoConn) NeedAdditionalReadDeadline() bool {
return c.Conn == nil
}
func (c *tfoConn) NeedHandshake() bool {
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) {
ctx, cancel := context.WithCancel(ctx)
dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false}

View File

@ -1,17 +1,13 @@
package geodata
import (
"context"
"fmt"
"io"
"net/http"
"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"
"io"
"net/http"
"os"
)
var initGeoSite bool
@ -42,9 +38,7 @@ func InitGeoSite() error {
}
func downloadGeoSite(path string) (err error) {
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)
resp, err := http.Get(C.GeoSiteUrl)
if err != nil {
return
}
@ -61,9 +55,7 @@ func downloadGeoSite(path string) (err error) {
}
func downloadGeoIP(path string) (err error) {
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)
resp, err := http.Get(C.GeoIpUrl)
if err != nil {
return
}

View File

@ -53,7 +53,7 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
conn := inner.HandleTcp(address, "")
conn := inner.HandleTcp(address, urlRes.Hostname())
return conn, nil
},
TLSClientConfig: tls.GetDefaultTLSConfig(),

View File

@ -1,18 +1,14 @@
package mmdb
import (
"context"
"github.com/oschwald/geoip2-golang"
"io"
"net/http"
"os"
"sync"
"time"
clashHttp "github.com/Dreamacro/clash/component/http"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/oschwald/geoip2-golang"
)
var (
@ -51,9 +47,7 @@ func Instance() *geoip2.Reader {
}
func DownloadMMDB(path string) (err error) {
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)
resp, err := http.Get(C.MmdbUrl)
if err != nil {
return
}

View File

@ -1,7 +1,7 @@
package profile
import (
"github.com/Dreamacro/clash/common/atomic"
"go.uber.org/atomic"
)
// StoreSelected is a global switch for storing selected proxy to cache

View File

@ -1,96 +0,0 @@
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
}

View File

@ -44,8 +44,10 @@ type Resolver interface {
LookupIP(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)
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)
Invalid() bool
}
// LookupIPv4WithResolver same as LookupIPv4, but with a resolver
@ -66,7 +68,7 @@ func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]net
return []netip.Addr{}, ErrIPVersion
}
if r != nil && r.Invalid() {
if r != nil {
return r.LookupIPv4(ctx, host)
}
@ -122,7 +124,7 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net
return nil, ErrIPVersion
}
if r != nil && r.Invalid() {
if r != nil {
return r.LookupIPv6(ctx, host)
}
@ -162,7 +164,7 @@ func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip
return node.IPs, nil
}
if r != nil && r.Invalid() {
if r != nil {
if DisableIPv6 {
return r.LookupIPv4(ctx, host)
}
@ -198,14 +200,10 @@ func ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip.
} else if len(ips) == 0 {
return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host)
}
ipv4s, ipv6s := SortationAddr(ips)
if len(ipv4s) > 0 {
return ipv4s[fastrand.Intn(len(ipv4s))], nil
}
return ipv6s[fastrand.Intn(len(ipv6s))], nil
return ips[fastrand.Intn(len(ips))], nil
}
// ResolveIP with a host, return ip and priority return TypeA
// ResolveIP with a host, return ip
func ResolveIP(ctx context.Context, host string) (netip.Addr, error) {
return ResolveIPWithResolver(ctx, host, DefaultResolver)
}
@ -266,14 +264,3 @@ func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, er
}
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
}

View File

@ -26,14 +26,14 @@ var (
var Dispatcher *SnifferDispatcher
type SnifferDispatcher struct {
enable bool
sniffers map[sniffer.Sniffer]SnifferConfig
forceDomain *trie.DomainSet
skipSNI *trie.DomainSet
skipList *cache.LruCache[string, uint8]
rwMux sync.RWMutex
forceDnsMapping bool
parsePureIp bool
enable bool
sniffers map[sniffer.Sniffer]SnifferConfig
forceDomain *trie.DomainSet
skipSNI *trie.DomainSet
skipList *cache.LruCache[string, uint8]
rwMux sync.RWMutex
forceDnsMapping bool
parsePureIp bool
}
func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) {

View File

@ -25,7 +25,7 @@ func ValidAndSplitDomain(domain string) ([]string, bool) {
if domain != "" && domain[len(domain)-1] == '.' {
return nil, false
}
domain = strings.ToLower(domain)
domain=strings.ToLower(domain)
parts := strings.Split(domain, domainStep)
if len(parts) == 1 {
if parts[0] == "" {
@ -126,9 +126,6 @@ func (t *DomainTrie[T]) 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)
}
}
}

View File

@ -1,17 +1,16 @@
package trie_test
package trie
import (
"net/netip"
"testing"
"github.com/Dreamacro/clash/component/trie"
"github.com/stretchr/testify/assert"
)
var localIP = netip.AddrFrom4([4]byte{127, 0, 0, 1})
func TestTrie_Basic(t *testing.T) {
tree := trie.New[netip.Addr]()
tree := New[netip.Addr]()
domains := []string{
"example.com",
"google.com",
@ -19,7 +18,7 @@ func TestTrie_Basic(t *testing.T) {
}
for _, domain := range domains {
assert.NoError(t, tree.Insert(domain, localIP))
tree.Insert(domain, localIP)
}
node := tree.Search("example.com")
@ -32,7 +31,7 @@ func TestTrie_Basic(t *testing.T) {
}
func TestTrie_Wildcard(t *testing.T) {
tree := trie.New[netip.Addr]()
tree := New[netip.Addr]()
domains := []string{
"*.example.com",
"sub.*.example.com",
@ -48,7 +47,7 @@ func TestTrie_Wildcard(t *testing.T) {
}
for _, domain := range domains {
assert.NoError(t, tree.Insert(domain, localIP))
tree.Insert(domain, localIP)
}
assert.NotNil(t, tree.Search("sub.example.com"))
@ -65,7 +64,7 @@ func TestTrie_Wildcard(t *testing.T) {
}
func TestTrie_Priority(t *testing.T) {
tree := trie.New[int]()
tree := New[int]()
domains := []string{
".dev",
"example.dev",
@ -80,7 +79,7 @@ func TestTrie_Priority(t *testing.T) {
}
for idx, domain := range domains {
assert.NoError(t, tree.Insert(domain, idx+1))
tree.Insert(domain, idx+1)
}
assertFn("test.dev", 1)
@ -91,8 +90,8 @@ func TestTrie_Priority(t *testing.T) {
}
func TestTrie_Boundary(t *testing.T) {
tree := trie.New[netip.Addr]()
assert.NoError(t, tree.Insert("*.dev", localIP))
tree := New[netip.Addr]()
tree.Insert("*.dev", localIP)
assert.NotNil(t, tree.Insert(".", localIP))
assert.NotNil(t, tree.Insert("..dev", localIP))
@ -100,15 +99,15 @@ func TestTrie_Boundary(t *testing.T) {
}
func TestTrie_WildcardBoundary(t *testing.T) {
tree := trie.New[netip.Addr]()
assert.NoError(t, tree.Insert("+.*", localIP))
assert.NoError(t, tree.Insert("stun.*.*.*", localIP))
tree := New[netip.Addr]()
tree.Insert("+.*", localIP)
tree.Insert("stun.*.*.*", localIP)
assert.NotNil(t, tree.Search("example.com"))
}
func TestTrie_Foreach(t *testing.T) {
tree := trie.New[netip.Addr]()
tree := New[netip.Addr]()
domainList := []string{
"google.com",
"stun.*.*.*",
@ -118,7 +117,7 @@ func TestTrie_Foreach(t *testing.T) {
"*.*.baidu.com",
}
for _, domain := range domainList {
assert.NoError(t, tree.Insert(domain, localIP))
tree.Insert(domain, localIP)
}
count := 0
tree.Foreach(func(domain string, data netip.Addr) {

View File

@ -7,36 +7,22 @@ import (
"github.com/stretchr/testify/assert"
)
func TestDomainSet(t *testing.T) {
tree := trie.New[struct{}]()
func TestDomain(t *testing.T) {
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()
set := trie.NewDomainSet(domainSet)
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{}]()
func TestDomainComplexWildcard(t *testing.T) {
domainSet := []string{
"+.baidu.com",
"+.a.baidu.com",
@ -46,40 +32,29 @@ func TestDomainSetComplexWildcard(t *testing.T) {
"test.a.oc",
"www.qq.com",
}
for _, domain := range domainSet {
assert.NoError(t, tree.Insert(domain, struct{}{}))
}
set := tree.NewDomainSet()
set := trie.NewDomainSet(domainSet)
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{}]()
func TestDomainWildcard(t *testing.T) {
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()
set := trie.NewDomainSet(domainSet)
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.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("www.google.com"))
assert.False(t, set.Has("test.qq.com"))
assert.False(t, set.Has("test.test.test.qq.com"))
}

View File

@ -23,16 +23,20 @@ type DomainSet struct {
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) {
// NewDomainSet creates a new *DomainSet struct, from a slice of sorted strings.
func NewDomainSet(keys []string) *DomainSet {
domainTrie := New[struct{}]()
for _, domain := range keys {
domainTrie.Insert(domain, struct{}{})
}
reserveDomains := make([]string, 0, len(keys))
domainTrie.Foreach(func(domain string, data struct{}) {
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
keys = reserveDomains
if len(keys) == 0 {
return nil
}
@ -100,11 +104,11 @@ func (ss *DomainSet) Has(key string) bool {
if j == len(key) {
if getBit(ss.leaves, nextNodeId) != 0 {
return true
} else {
}else {
goto RESTART
}
}
for ; nextBmIdx-nextNodeId < len(ss.labels); nextBmIdx++ {
for ; ; nextBmIdx++ {
if ss.labels[nextBmIdx-nextNodeId] == domainStepByte {
bmIdx = nextBmIdx
nodeId = nextNodeId

View File

@ -841,7 +841,7 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) {
} else {
ips := make([]netip.Addr, 0)
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback()&&!ipnet.IP.IsLinkLocalUnicast() {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ip, err := netip.ParseAddr(ipnet.IP.String()); err == nil {
ips = append(ips, ip)
}
@ -896,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())
}
proxyName := u.Fragment
proxyAdapter := u.Fragment
var addr, dnsNetType string
params := map[string]string{}
@ -913,7 +913,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
case "https":
addr, err = hostWithDefaultPort(u.Host, "443")
if err == nil {
proxyName = ""
proxyAdapter = ""
clearURL := url.URL{Scheme: "https", Host: addr, Path: u.Path}
addr = clearURL.String()
dnsNetType = "https" // DNS over HTTPS
@ -923,7 +923,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
if len(arr) == 0 {
continue
} else if len(arr) == 1 {
proxyName = arr[0]
proxyAdapter = arr[0]
} else if len(arr) == 2 {
params[arr[0]] = arr[1]
} else {
@ -949,24 +949,18 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
nameservers = append(
nameservers,
dns.NameServer{
Net: dnsNetType,
Addr: addr,
ProxyName: proxyName,
Interface: dialer.DefaultInterface,
Params: params,
PreferH3: preferH3,
Net: dnsNetType,
Addr: addr,
ProxyAdapter: proxyAdapter,
Interface: dialer.DefaultInterface,
Params: params,
PreferH3: preferH3,
},
)
}
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 {
addPre := func(server string) string {
return "udp://" + server
@ -1346,25 +1340,8 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
}
sniffer.Sniffers = loadSniffer
forceDomainTrie := trie.New[struct{}]()
for _, domain := range snifferRaw.ForceDomain {
err := forceDomainTrie.Insert(domain, struct{}{})
if err != nil {
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
}
}
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()
sniffer.ForceDomain = trie.NewDomainSet(snifferRaw.ForceDomain)
sniffer.SkipDomain = trie.NewDomainSet(snifferRaw.SkipDomain)
return sniffer, nil
}

View File

@ -1,20 +1,15 @@
package config
import (
"context"
"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"
"net/http"
"os"
"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 {
@ -74,9 +69,7 @@ func UpdateGeoDatabases() error {
}
func downloadForBytes(url string) ([]byte, error) {
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)
resp, err := http.Get(url)
if err != nil {
return nil, err
}

View File

@ -3,7 +3,6 @@ package config
import (
"fmt"
"net"
"net/netip"
"strings"
"github.com/Dreamacro/clash/adapter/outboundgroup"
@ -150,11 +149,20 @@ func proxyGroupsDagSort(groupsConfig []map[string]any) error {
}
func verifyIP6() bool {
if iAddrs, err := net.InterfaceAddrs(); err == nil {
for _, addr := range iAddrs {
if prefix, err := netip.ParsePrefix(addr.String()); err == nil {
if addr := prefix.Addr().Unmap(); addr.Is6() && addr.IsGlobalUnicast() {
return true
addrs, err := net.InterfaceAddrs()
if err != nil {
return false
}
for _, addr := range addrs {
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
}
}
}
}

View File

@ -2,14 +2,12 @@ package constant
import (
"context"
"errors"
"fmt"
"net"
"net/netip"
"sync"
"time"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/component/dialer"
)
@ -45,8 +43,6 @@ const (
DefaultTLSTimeout = DefaultTCPTimeout
)
var ErrNotSupport = errors.New("no support")
type Connection interface {
Chains() Chain
AppendToChains(adapter ProxyAdapter)
@ -76,7 +72,7 @@ func (c Chain) Last() string {
}
type Conn interface {
N.ExtendedConn
net.Conn
Connection
}
@ -120,13 +116,10 @@ type ProxyAdapter interface {
// SupportUOT return UDP over TCP support
SupportUOT() bool
SupportWithDialer() NetWork
SupportWithDialer() bool
DialContextWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (Conn, 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(metadata *Metadata, touch bool) Proxy
}

View File

@ -5,7 +5,7 @@ import (
N "github.com/Dreamacro/clash/common/net"
"github.com/gofrs/uuid/v5"
"github.com/gofrs/uuid"
)
type PlainContext interface {

View File

@ -1,4 +1,3 @@
//go:build with_low_memory
package features
func init() {

View File

@ -15,10 +15,7 @@ const (
TCP NetWork = iota
UDP
ALLNet
InvalidNet = 0xff
)
const (
HTTP Type = iota
HTTPS
SOCKS4
@ -36,16 +33,12 @@ const (
type NetWork int
func (n NetWork) String() string {
switch n {
case TCP:
if n == TCP {
return "tcp"
case UDP:
} else if n == UDP {
return "udp"
case ALLNet:
return "all"
default:
return "invalid"
}
return "all"
}
func (n NetWork) MarshalJSON() ([]byte, error) {
@ -229,21 +222,3 @@ func (m *Metadata) String() string {
func (m *Metadata) Valid() bool {
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
}

View File

@ -66,7 +66,7 @@ func (p *path) MMDB() string {
// 目录则直接跳过
continue
} else {
if strings.EqualFold(fi.Name(), "Country.mmdb") {
if strings.EqualFold(strings.ToLower(fi.Name()), "country.mmdb") {
GeoipName = fi.Name()
return P.Join(p.homeDir, fi.Name())
}
@ -93,7 +93,7 @@ func (p *path) GeoIP() string {
// 目录则直接跳过
continue
} else {
if strings.EqualFold(fi.Name(), "GeoIP.dat") {
if strings.EqualFold(strings.ToLower(fi.Name()), "geoip.dat") {
GeoipName = fi.Name()
return P.Join(p.homeDir, fi.Name())
}
@ -112,7 +112,7 @@ func (p *path) GeoSite() string {
// 目录则直接跳过
continue
} else {
if strings.EqualFold(fi.Name(), "GeoSite.dat") {
if strings.EqualFold(strings.ToLower(fi.Name()), "geosite.dat") {
GeositeName = fi.Name()
return P.Join(p.homeDir, fi.Name())
}

View File

@ -73,27 +73,17 @@ type ProxyProvider interface {
Version() uint32
}
// RuleProvider interface
type RuleProvider interface {
Provider
Behavior() RuleBehavior
Match(*constant.Metadata) bool
ShouldResolveIP() bool
ShouldFindProcess() bool
AsRule(adaptor string) constant.Rule
}
// Rule Behavior
// Rule Type
const (
Domain RuleBehavior = iota
Domain RuleType = iota
IPCIDR
Classical
)
// RuleBehavior defined
type RuleBehavior int
// RuleType defined
type RuleType int
func (rt RuleBehavior) String() string {
func (rt RuleType) String() string {
switch rt {
case Domain:
return "Domain"
@ -106,20 +96,12 @@ func (rt RuleBehavior) String() string {
}
}
const (
YamlRule RuleFormat = iota
TextRule
)
type RuleFormat int
func (rf RuleFormat) String() string {
switch rf {
case YamlRule:
return "YamlRule"
case TextRule:
return "TextRule"
default:
return "Unknown"
}
// RuleProvider interface
type RuleProvider interface {
Provider
Behavior() RuleType
Match(*constant.Metadata) bool
ShouldResolveIP() bool
ShouldFindProcess() bool
AsRule(adaptor string) constant.Rule
}

View File

@ -7,7 +7,7 @@ import (
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
"github.com/gofrs/uuid/v5"
"github.com/gofrs/uuid"
)
type ConnContext struct {

View File

@ -4,7 +4,7 @@ import (
"context"
"github.com/Dreamacro/clash/common/utils"
"github.com/gofrs/uuid/v5"
"github.com/gofrs/uuid"
"github.com/miekg/dns"
)

View File

@ -6,7 +6,7 @@ import (
"github.com/Dreamacro/clash/common/utils"
C "github.com/Dreamacro/clash/constant"
"github.com/gofrs/uuid/v5"
"github.com/gofrs/uuid"
)
type PacketConnContext struct {

View File

@ -8,11 +8,11 @@ import (
"net/netip"
"strings"
"github.com/Dreamacro/clash/common/atomic"
tlsC "github.com/Dreamacro/clash/component/tls"
"go.uber.org/atomic"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/resolver"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
D "github.com/miekg/dns"
"github.com/zhangyunhao116/fastrand"
@ -23,9 +23,8 @@ type client struct {
r *Resolver
port string
host string
iface *atomic.TypedValue[string]
proxyAdapter C.ProxyAdapter
proxyName string
iface *atomic.String
proxyAdapter string
addr string
}
@ -82,7 +81,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
options = append(options, dialer.WithInterface(c.iface.Load()))
}
conn, err := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port))
conn, err := getDialHandler(c.r, c.proxyAdapter, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port))
if err != nil {
return nil, err
}

View File

@ -8,7 +8,8 @@ import (
"sync"
"time"
"github.com/Dreamacro/clash/common/atomic"
"go.uber.org/atomic"
"github.com/Dreamacro/clash/component/dhcp"
"github.com/Dreamacro/clash/component/iface"
"github.com/Dreamacro/clash/component/resolver"
@ -85,7 +86,7 @@ func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) {
for _, item := range dns {
nameserver = append(nameserver, NameServer{
Addr: net.JoinHostPort(item.String(), "53"),
Interface: atomic.NewTypedValue(d.ifaceName),
Interface: atomic.NewString(d.ifaceName),
})
}

View File

@ -21,7 +21,6 @@ import (
"github.com/metacubex/quic-go"
"github.com/metacubex/quic-go/http3"
D "github.com/miekg/dns"
"golang.org/x/exp/slices"
"golang.org/x/net/http2"
)
@ -64,8 +63,7 @@ type dnsOverHTTPS struct {
url *url.URL
r *Resolver
httpVersions []C.HTTPVersion
proxyAdapter C.ProxyAdapter
proxyName string
proxyAdapter string
addr string
}
@ -73,7 +71,7 @@ type dnsOverHTTPS struct {
var _ dnsClient = (*dnsOverHTTPS)(nil)
// newDoH returns the DNS-over-HTTPS Upstream.
func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) dnsClient {
func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter string) dnsClient {
u, _ := url.Parse(urlString)
httpVersions := DefaultHTTPVersions
if preferH3 {
@ -89,7 +87,6 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin
addr: u.String(),
r: r,
proxyAdapter: proxyAdapter,
proxyName: proxyName,
quicConfig: &quic.Config{
KeepAlivePeriod: QUICKeepAlivePeriod,
TokenStore: newQUICTokenStore(),
@ -393,17 +390,14 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
nextProtos = append(nextProtos, string(v))
}
tlsConfig.NextProtos = nextProtos
dialContext := getDialHandler(doh.r, doh.proxyAdapter, doh.proxyName)
if slices.Contains(doh.httpVersions, C.HTTPVersion3) {
// First, we attempt to create an HTTP3 transport. If the probe QUIC
// connection is established successfully, we'll be using HTTP3 for this
// upstream.
transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext)
if err == nil {
log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String())
return transportH3, nil
}
dialContext := getDialHandler(doh.r, doh.proxyAdapter)
// First, we attempt to create an HTTP3 transport. If the probe QUIC
// connection is established successfully, we'll be using HTTP3 for this
// upstream.
transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext)
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)
@ -539,7 +533,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.
IP: net.ParseIP(ip),
Port: portInt,
}
conn, err := listenPacket(ctx, doh.proxyAdapter, doh.proxyName, "udp", addr, doh.r)
conn, err := listenPacket(ctx, doh.proxyAdapter, "udp", addr, doh.r)
if err != nil {
return nil, err
}

View File

@ -13,10 +13,9 @@ import (
"time"
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/Dreamacro/clash/log"
D "github.com/miekg/dns"
)
@ -61,8 +60,7 @@ type dnsOverQUIC struct {
bytesPoolGuard sync.Mutex
addr string
proxyAdapter C.ProxyAdapter
proxyName string
proxyAdapter string
r *Resolver
}
@ -70,11 +68,10 @@ type dnsOverQUIC struct {
var _ dnsClient = (*dnsOverQUIC)(nil)
// newDoQ returns the DNS-over-QUIC Upstream.
func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) {
func newDoQ(resolver *Resolver, addr string, adapter string) (dnsClient, error) {
doq := &dnsOverQUIC{
addr: addr,
proxyAdapter: proxyAdapter,
proxyName: proxyName,
proxyAdapter: adapter,
r: resolver,
quicConfig: &quic.Config{
KeepAlivePeriod: QUICKeepAlivePeriod,
@ -313,7 +310,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
// 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
// what IP is actually reachable (when there're v4/v6 addresses).
rawConn, err := getDialHandler(doq.r, doq.proxyAdapter, doq.proxyName)(ctx, "udp", doq.addr)
rawConn, err := getDialHandler(doq.r, doq.proxyAdapter)(ctx, "udp", doq.addr)
if err != nil {
return nil, fmt.Errorf("failed to open a QUIC connection: %w", err)
}
@ -328,7 +325,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
p, err := strconv.Atoi(port)
udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p}
udp, err := listenPacket(ctx, doq.proxyAdapter, doq.proxyName, "udp", addr, doq.r)
udp, err := listenPacket(ctx, doq.proxyAdapter, "udp", addr, doq.r)
if err != nil {
return nil, err
}

View File

@ -3,11 +3,13 @@ package dns
import (
"context"
"errors"
"fmt"
"net/netip"
"strings"
"time"
"github.com/Dreamacro/clash/common/atomic"
"go.uber.org/atomic"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/component/fakeip"
"github.com/Dreamacro/clash/component/geodata/router"
@ -18,6 +20,7 @@ import (
"github.com/Dreamacro/clash/log"
D "github.com/miekg/dns"
"github.com/zhangyunhao116/fastrand"
"golang.org/x/sync/singleflight"
)
@ -97,7 +100,7 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr,
ips, err = r.lookupIP(ctx, host, D.TypeA)
var waitIPv6 *time.Timer
if r != nil && r.ipv6Timeout > 0 {
if r != nil {
waitIPv6 = time.NewTimer(r.ipv6Timeout)
} else {
waitIPv6 = time.NewTimer(100 * time.Millisecond)
@ -116,16 +119,49 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr,
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
func (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) {
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
func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) {
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 {
for _, filter := range r.fallbackIPFilters {
if filter.Match(ip) {
@ -376,20 +412,16 @@ func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D
return ch
}
// Invalid return this resolver can or can't be used
func (r *Resolver) Invalid() bool {
if r == nil {
return false
}
// HasProxyServer has proxy server dns client
func (r *Resolver) HasProxyServer() bool {
return len(r.main) > 0
}
type NameServer struct {
Net string
Addr string
Interface *atomic.TypedValue[string]
ProxyAdapter C.ProxyAdapter
ProxyName string
Interface *atomic.String
ProxyAdapter string
Params map[string]string
PreferH3 bool
}
@ -512,5 +544,3 @@ func NewProxyServerHostResolver(old *Resolver) *Resolver {
}
return r
}
var ParseNameServer func(servers []string) ([]NameServer, error) // define in config/config.go

View File

@ -74,13 +74,13 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
for _, s := range servers {
switch s.Net {
case "https":
ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName))
ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter))
continue
case "dhcp":
ret = append(ret, newDHCPClient(s.Addr))
continue
case "quic":
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter, s.ProxyName); err == nil {
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter); err == nil {
ret = append(ret, doq)
} else {
log.Fatalln("DoQ format error: %v", err)
@ -103,7 +103,6 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
iface: s.Interface,
r: resolver,
proxyAdapter: s.ProxyAdapter,
proxyName: s.ProxyName,
})
}
return ret
@ -145,9 +144,9 @@ func msgToDomain(msg *D.Msg) string {
type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler {
func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dialHandler {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
if len(proxyName) == 0 && proxyAdapter == nil {
if len(proxyAdapter) == 0 {
opts = append(opts, dialer.WithResolver(r))
return dialer.DialContext(ctx, network, addr, opts...)
} else {
@ -155,14 +154,10 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
if err != nil {
return nil, err
}
if proxyAdapter == nil {
var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName]
if !ok {
opts = append(opts, dialer.WithInterface(proxyName))
}
adapter, ok := tunnel.Proxies()[proxyAdapter]
if !ok {
opts = append(opts, dialer.WithInterface(proxyAdapter))
}
if strings.Contains(network, "tcp") {
// tcp can resolve host by remote
metadata := &C.Metadata{
@ -170,16 +165,8 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
Host: host,
DstPort: port,
}
if proxyAdapter != nil {
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...)
if ok {
return adapter.DialContext(ctx, metadata, opts...)
}
opts = append(opts, dialer.WithResolver(r))
return dialer.DialContext(ctx, network, addr, opts...)
@ -195,15 +182,15 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
DstIP: dstIP,
DstPort: port,
}
if proxyAdapter == nil {
if !ok {
return dialer.DialContext(ctx, network, addr, opts...)
}
if !proxyAdapter.SupportUDP() {
if !adapter.SupportUDP() {
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
}
packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...)
if err != nil {
return nil, err
}
@ -214,17 +201,14 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
}
}
func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
func listenPacket(ctx context.Context, proxyAdapter string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
if proxyAdapter == nil {
var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName]
if !ok {
opts = append(opts, dialer.WithInterface(proxyName))
}
adapter, ok := tunnel.Proxies()[proxyAdapter]
if !ok && len(proxyAdapter) != 0 {
opts = append(opts, dialer.WithInterface(proxyAdapter))
}
// udp must resolve host first
@ -238,15 +222,15 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st
DstIP: dstIP,
DstPort: port,
}
if proxyAdapter == nil {
if !ok {
return dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...)
}
if !proxyAdapter.SupportUDP() {
if !adapter.SupportUDP() {
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
}
return proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
return adapter.ListenPacketContext(ctx, metadata, opts...)
}
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {

2387
docs/allocs.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 127 KiB

View File

@ -238,9 +238,7 @@ dns:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
"www.baidu.com,+.google.cn": [223.5.5.5, https://dns.alidns.com/dns-query]
## globaldns 为 rule-providers 中的名为 global 和 dns 规则订阅,
## 且 behavior 必须为 domain/classical当为 classical 时仅会生效域名类规则
# "rule-set:global,dns": 8.8.8.8
# "rule-set:global,dns": 8.8.8.8 # globaldns 为 rule-providers 中的名为 global 和 dns 规则提供器名字,且 behavior 必须为 domain
proxies: # socks5
- name: "socks"
@ -302,15 +300,6 @@ proxies: # socks5
# UDP 则为双栈解析,获取结果中的第一个 IPv4
# ipv6-prefer 同 ipv4-prefer
# 现有协议都支持此参数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"
type: ss
@ -603,7 +592,6 @@ proxies: # socks5
type: hysteria
server: server.com
port: 443
# ports: 1000,2000-3000,5000 # port 不可省略,
auth_str: yourpassword # 将会在未来某个时候删除
# auth-str: yourpassword
# obfs: obfs_str
@ -632,27 +620,12 @@ proxies: # socks5
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=
private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU=
public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=
udp: true
reserved: "U4An"
# 数组格式也是合法的
# 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
- name: tuic
@ -694,9 +667,7 @@ proxies: # socks5
# protocol-param: "#"
# udp: true
proxy-groups:
# 代理链目前relay可以支持udp的只有vmess/vless/trojan/ss/ssr/tuic
# wireguard目前不支持在relay中使用请使用proxy中的dialer-proxy配置项
proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP
# Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet
- name: "relay"
type: relay

2182
docs/heap.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 116 KiB

71
go.mod
View File

@ -11,97 +11,80 @@ require (
github.com/go-chi/chi/v5 v5.0.8
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.2
github.com/gofrs/uuid/v5 v5.0.0
github.com/gofrs/uuid v4.4.0+incompatible
github.com/google/gopacket v1.1.19
github.com/gorilla/websocket v1.5.0
github.com/hashicorp/golang-lru v0.5.4
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
github.com/jpillora/backoff v1.0.0
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7
github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594
github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba
github.com/metacubex/sing-tun v0.1.4
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a
github.com/miekg/dns v1.1.53
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947
github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3
github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb
github.com/miekg/dns v1.1.52
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/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9
github.com/sagernet/sing-mux v0.0.0-20230427141602-9836fc9b052e
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/sing v0.2.1-0.20230323071235-f8038854d286
github.com/sagernet/sing-shadowtls v0.1.0
github.com/sagernet/sing-vmess v0.1.3
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77
github.com/samber/lo v1.38.1
github.com/shirou/gopsutil/v3 v3.23.3
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
github.com/samber/lo v1.37.0
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.2
github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837
github.com/zhangyunhao116/fastrand v0.3.0
go.etcd.io/bbolt v1.3.6
go.uber.org/automaxprocs v1.5.2
golang.org/x/crypto v0.8.0
go.uber.org/atomic v1.10.0
go.uber.org/automaxprocs v1.5.1
golang.org/x/crypto v0.7.0
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
golang.org/x/net v0.9.0
golang.org/x/net v0.8.0
golang.org/x/sync v0.1.0
golang.org/x/sys v0.7.0
golang.org/x/sys v0.6.0
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.1.7
)
require (
github.com/RyuaNerin/go-krypto v1.0.2 // indirect
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
)
require (
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // 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/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/go-cmp v0.5.9 // 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/klauspost/compress v1.15.15 // 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/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/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/openacid/low v0.1.21
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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // 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-20 v0.1.1 // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // 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/u-root/uio v0.0.0-20221213070652-c3537552635f // 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/text v0.9.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // 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

173
go.sum
View File

@ -1,9 +1,5 @@
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/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/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
@ -23,15 +19,7 @@ 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/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
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/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
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/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -41,12 +29,10 @@ 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/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-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/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
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/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -54,29 +40,37 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
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/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.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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
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/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/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E=
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
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/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
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/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/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@ -86,32 +80,37 @@ 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/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/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/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/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c h1:D62872jiuzC6b+3aI8tqfeyc6YgbfarYKywTnnvXwEM=
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE=
github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 h1:gREIdurac9fpyBMBRPPMF/Sk3gKfPfdNCa4GQyR9FoA=
github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03/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/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA=
github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba h1:He8YwyK600lHAS1xxNsP4k/jnZ8zqQ34XjCGn925+Yk=
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.4 h1:OQDBNHjuPKrOprCiK+sLt97YQ0K6b9ZWmJB6z51ibZQ=
github.com/metacubex/sing-tun v0.1.4/go.mod h1:BMfG00enVf90/CzcdX9PK3Dymgl7BZqHXJfexEyB7Cc=
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a h1:cWKym33Qvl6HA3hj4/YuYD8hHyqQPb47wT5cJRAPgco=
github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY=
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 h1:NnjC2+aIiyzzvFlo+C2WzBOJdsp+HAtu18FZomqYhUE=
github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947/go.mod h1:U2gwhxzqgbhKCgn2B4z3t0Cj0LpMWFl/02BGCoG421w=
github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3 h1:LnKcLs0HI0HX4xH/2XerX+1BLXS1Uj6Xvzn20xFuCOk=
github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3/go.mod h1:0i22nk0tgkQz/N96hrhPib1O/C5AjxSnco7Mwi2YSF0=
github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb h1:uhvzbtOvyg2c1k1H2EeVPuPvTEjDHCq4+U0AljG40P8=
github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM=
github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo=
github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
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/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/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
@ -124,13 +123,9 @@ github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6
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/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/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/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
@ -144,40 +139,24 @@ 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/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.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9 h1:kpgKJbhesj6BBLTKIfBCJGQPm2ww7pNxn566C6TrHdA=
github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-mux v0.0.0-20230427141602-9836fc9b052e h1:t8nuY9plpHEzlnPxOpuv64jhjz3teIvccu3YMFX4fJI=
github.com/sagernet/sing-mux v0.0.0-20230427141602-9836fc9b052e/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
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/sing v0.2.1-0.20230323071235-f8038854d286 h1:0Td2b5l1KgrdlOnbRWgFFWsyb0TLoq/tP6j9Lut4JN0=
github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM=
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE=
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/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/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
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/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw=
github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -189,31 +168,24 @@ 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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
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/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
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/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M=
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/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/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/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk=
go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=
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.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
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/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
@ -221,43 +193,56 @@ 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.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
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-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-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.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
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.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
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-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-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/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-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-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-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-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-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-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
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/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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.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/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-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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=

View File

@ -239,7 +239,7 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen
resolver.DefaultHostMapper = m
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
if pr.Invalid() {
if pr.HasProxyServer() {
resolver.ProxyServerHostResolver = pr
}
@ -413,7 +413,7 @@ func patchSelectGroup(proxies map[string]C.Proxy) {
continue
}
selector.ForceSet(selected)
selector.Set(selected)
}
}

View File

@ -8,7 +8,6 @@ import (
"runtime"
"syscall"
"github.com/Dreamacro/clash/listener"
"github.com/Dreamacro/clash/log"
"github.com/go-chi/chi/v5"
@ -39,29 +38,25 @@ func restart(w http.ResponseWriter, r *http.Request) {
// The background context is used because the underlying functions wrap 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.
go runRestart(execPath)
}
go func() {
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:])
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
err = syscall.Exec(execPath, os.Args, os.Environ())
if err != nil {
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)
}
}()
}

View File

@ -39,11 +39,6 @@ type Traffic struct {
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) {
uiPath = C.Path.Resolve(path)
}
@ -81,7 +76,6 @@ func Start(addr string, tlsAddr string, secret string,
r.Get("/", hello)
r.Get("/logs", getLogs)
r.Get("/traffic", traffic)
r.Get("/memory", memory)
r.Get("/version", version)
r.Mount("/configs", configRouter())
r.Mount("/proxies", proxyRouter())
@ -230,56 +224,6 @@ 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 string `json:"type"`
Payload string `json:"payload"`

View File

@ -4,6 +4,9 @@ import (
"fmt"
"net/http"
"os"
"os/exec"
"runtime"
"syscall"
"github.com/Dreamacro/clash/hub/updater"
"github.com/Dreamacro/clash/log"
@ -21,6 +24,12 @@ func upgradeRouter() http.Handler {
func upgrade(w http.ResponseWriter, r *http.Request) {
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108
log.Infoln("start update")
err := updater.Update()
if err != nil {
log.Errorln("err:%s", err)
return
}
execPath, err := os.Executable()
if err != nil {
render.Status(r, http.StatusInternalServerError)
@ -28,17 +37,34 @@ func upgrade(w http.ResponseWriter, r *http.Request) {
return
}
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)
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180
// The background context is used because the underlying functions wrap 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.
go func() {
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)
}
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)
}
}()
}

View File

@ -22,6 +22,8 @@ import (
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/updater/updater.go
// Updater is the Clash.Meta updater.
var (
client http.Client
goarch string
goos string
goarm string
@ -32,15 +34,18 @@ var (
// mu protects all fields below.
mu sync.RWMutex
// TODO(a.garipov): See if all of these fields actually have to be in
// this struct.
currentExeName string // 当前可执行文件
updateDir string // 更新目录
packageName string // 更新压缩文件
backupDir string // 备份目录
backupExeName string // 备份文件名
updateExeName string // 更新后的可执行文件
unpackedFile string
baseURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta"
versionURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/version.txt"
baseURL string = "https://testingcf.jsdelivr.net/gh/MetaCubeX/Clash.Meta@release/clash.meta"
versionURL string = "https://raw.githubusercontent.com/MetaCubeX/Clash.Meta/release/version.txt"
packageURL string
latestVersion string
)
@ -50,30 +55,30 @@ type updateError struct {
}
func (e *updateError) Error() string {
return fmt.Sprintf("update error: %s", e.Message)
return fmt.Sprintf("error: %s", e.Message)
}
// 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.
func Update(execPath string) (err error) {
mu.Lock()
defer mu.Unlock()
func Update() (err error) {
goos = runtime.GOOS
goarch = runtime.GOARCH
latestVersion, err = getLatestVersion()
if err != nil {
err := &updateError{Message: err.Error()}
return err
}
log.Infoln("current version %s, latest version %s", constant.Version, latestVersion)
if latestVersion == constant.Version {
err := &updateError{Message: "Already using latest version"}
return err
}
updateDownloadURL()
mu.Lock()
defer mu.Unlock()
log.Infoln("current version alpha-%s", constant.Version)
defer func() {
if err != nil {
@ -83,7 +88,13 @@ func Update(execPath string) (err error) {
}
}()
execPath, err := os.Executable()
if err != nil {
return fmt.Errorf("getting executable path: %w", err)
}
workDir = filepath.Dir(execPath)
//log.Infoln("workDir %s", execPath)
err = prepare(execPath)
if err != nil {
@ -102,11 +113,6 @@ func Update(execPath string) (err error) {
return fmt.Errorf("unpacking: %w", err)
}
err = backup()
if err != nil {
return fmt.Errorf("backuping: %w", err)
}
err = replace()
if err != nil {
return fmt.Errorf("replacing: %w", err)
@ -128,15 +134,13 @@ func prepare(exePath string) (err error) {
//log.Infoln(packageName)
backupDir = filepath.Join(workDir, "meta-backup")
if goos == "windows" && goarch == "amd64" {
updateExeName = "clash.meta" + "-" + goos + "-" + goarch + "-compatible.exe"
} else if goos == "linux" && goarch == "amd64" {
updateExeName = "clash.meta" + "-" + goos + "-" + goarch + "-compatible"
if goos == "windows" {
updateExeName = "clash.meta" + "-" + goos + "-" + goarch + ".exe"
} else {
updateExeName = "clash.meta" + "-" + goos + "-" + goarch
}
log.Infoln("updateExeName: %s ", updateExeName)
log.Infoln("updateExeName: %s ,currentExeName: %s", updateExeName, currentExeName)
backupExeName = filepath.Join(backupDir, filepath.Base(exePath))
updateExeName = filepath.Join(updateDir, updateExeName)
@ -160,15 +164,15 @@ func unpack() error {
var err error
_, pkgNameOnly := filepath.Split(packageURL)
log.Infoln("updater: unpacking package")
log.Debugln("updater: unpacking package")
if strings.HasSuffix(pkgNameOnly, ".zip") {
_, err = zipFileUnpack(packageName, updateDir)
unpackedFile, err = zipFileUnpack(packageName, updateDir)
if err != nil {
return fmt.Errorf(".zip unpack failed: %w", err)
}
} else if strings.HasSuffix(pkgNameOnly, ".gz") {
_, err = gzFileUnpack(packageName, updateDir)
unpackedFile, err = gzFileUnpack(packageName, updateDir)
if err != nil {
return fmt.Errorf(".gz unpack failed: %w", err)
}
@ -180,26 +184,23 @@ func unpack() error {
return nil
}
// backup makes a backup of the current executable file
func backup() (err error) {
log.Infoln("updater: backing up current ExecFile:%s to %s", currentExeName, backupExeName)
_ = os.Mkdir(backupDir, 0o755)
// replace moves the current executable with the updated one and also copies the
// supporting files.
func replace() error {
//err := copySupportingFiles(unpackedFiles, updateDir, workDir)
//if err != nil {
// return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", updateDir, workDir, err)
//}
err = os.Rename(currentExeName, backupExeName)
log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName)
err := os.Rename(currentExeName, backupExeName)
if err != nil {
return err
}
return nil
}
// replace moves the current executable with the updated one
func replace() error {
var err error
log.Infoln("replacing: %s to %s", updateExeName, currentExeName)
if goos == "windows" {
// rename fails with "File in use" error
log.Infoln("copying:%s to %s", updateExeName, currentExeName)
err = copyFile(updateExeName, currentExeName)
} else {
err = os.Rename(updateExeName, currentExeName)
@ -208,8 +209,6 @@ func replace() error {
return err
}
log.Infoln("updater: renamed: %s to %s", updateExeName, currentExeName)
return nil
}
@ -225,6 +224,8 @@ const MaxPackageFileSize = 32 * 1024 * 1024
// Download package file and save it to disk
func downloadPackageFile() (err error) {
// var resp *http.Response
// resp, err = client.Get(packageURL)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
defer cancel()
resp, err := clashHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
@ -255,11 +256,11 @@ func downloadPackageFile() (err error) {
log.Debugln("updateDir %s", updateDir)
err = os.Mkdir(updateDir, 0o755)
if err != nil {
return fmt.Errorf("mkdir error: %w", err)
fmt.Errorf("mkdir error: %w", err)
}
log.Debugln("updater: saving package to file %s", packageName)
err = os.WriteFile(packageName, body, 0o644)
err = os.WriteFile(packageName, body, 0o755)
if err != nil {
return fmt.Errorf("os.WriteFile() failed: %w", err)
}
@ -424,6 +425,7 @@ func getLatestVersion() (version string, err error) {
return "", fmt.Errorf("get Latest Version fail: %w", err)
}
content := strings.TrimRight(string(body), "\n")
log.Infoln("latest:%s", content)
return content, nil
}
@ -434,8 +436,6 @@ func updateDownloadURL() {
middle = fmt.Sprintf("-%s-%sv%s-%s", goos, goarch, goarm, latestVersion)
} else if isMIPS(goarch) && gomips != "" {
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 {
middle = fmt.Sprintf("-%s-%s-%s", goos, goarch, latestVersion)
}

View File

@ -5,7 +5,6 @@ import (
"strings"
"github.com/Dreamacro/clash/adapter/inbound"
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
LC "github.com/Dreamacro/clash/listener/config"
"github.com/Dreamacro/clash/transport/shadowsocks/core"
@ -101,7 +100,6 @@ func (l *Listener) AddrList() (addrList []net.Addr) {
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
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))
if err != nil {

View File

@ -10,15 +10,12 @@ import (
"time"
"github.com/Dreamacro/clash/adapter/inbound"
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5"
mux "github.com/sagernet/sing-mux"
vmess "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/network"
@ -36,7 +33,7 @@ type ListenerHandler struct {
}
type waitCloseConn struct {
N.ExtendedConn
net.Conn
wg *sync.WaitGroup
close sync.Once
rAddr net.Addr
@ -46,7 +43,7 @@ func (c *waitCloseConn) Close() error { // call from handleTCPConn(connCtx C.Con
c.close.Do(func() {
c.wg.Done()
})
return c.ExtendedConn.Close()
return c.Conn.Close()
}
func (c *waitCloseConn) RemoteAddr() net.Addr {
@ -54,14 +51,7 @@ func (c *waitCloseConn) RemoteAddr() net.Addr {
}
func (c *waitCloseConn) Upstream() any {
return c.ExtendedConn
}
func UpstreamMetadata(metadata M.Metadata) M.Metadata {
return M.Metadata{
Source: metadata.Source,
Destination: metadata.Destination,
}
return c.Conn
}
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
@ -71,8 +61,6 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
additions = append(additions, ctxAdditions...)
}
switch metadata.Destination.Fqdn {
case mux.Destination.Fqdn:
return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata))
case vmess.MuxDestination.Fqdn:
return vmess.HandleMuxConnection(ctx, conn, h)
case uot.MagicAddress:
@ -91,10 +79,7 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
defer wg.Wait() // this goroutine must exit after conn.Close()
wg.Add(1)
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...)
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{Conn: conn, wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...)
return nil
}
@ -117,7 +102,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
dest, err := conn.ReadPacket(buff)
if err != nil {
buff.Release()
if ShouldIgnorePacketError(err) {
if E.IsClosed(err) {
break
}
return err
@ -142,14 +127,6 @@ func (h *ListenerHandler) NewError(ctx context.Context, err error) {
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 {
conn *network.PacketConn
mutex *sync.Mutex

View File

@ -17,6 +17,7 @@ import (
D "github.com/miekg/dns"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/network"
)
@ -116,7 +117,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
dest, err := conn.ReadPacket(buff)
if err != nil {
buff.Release()
if sing.ShouldIgnorePacketError(err) {
if E.IsClosed(err) {
break
}
return err

View File

@ -229,8 +229,8 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
err = E.Cause(err, "configure tun interface")
return
}
stackOptions := tun.StackOptions{
l.tunIf = tunIf
l.tunStack, err = tun.NewStack(strings.ToLower(options.Stack.String()), tun.StackOptions{
Context: context.TODO(),
Tun: tunIf,
MTU: tunOptions.MTU,
@ -241,16 +241,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
UDPTimeout: udpTimeout,
Handler: handler,
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 {
return
}

View File

@ -1,11 +0,0 @@
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 */
)
}

View File

@ -1,25 +0,0 @@
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
}

View File

@ -1,9 +0,0 @@
//go:build !(darwin || linux)
package sing_tun
import "os"
func getTunnelName(fd int32) (string, error) {
return "", os.ErrInvalid
}

53
patch/add_debug_api.patch Normal file
View File

@ -0,0 +1,53 @@
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)

View File

@ -37,37 +37,35 @@ func (c *classicalStrategy) ShouldFindProcess() bool {
return c.shouldFindProcess
}
func (c *classicalStrategy) Reset() {
c.rules = nil
c.count = 0
c.shouldFindProcess = false
c.shouldResolveIP = false
}
func (c *classicalStrategy) OnUpdate(rules []string) {
var classicalRules []C.Rule
shouldResolveIP := false
for _, rawRule := range rules {
ruleType, rule, params := ruleParse(rawRule)
func (c *classicalStrategy) Insert(rule string) {
ruleType, rule, params := ruleParse(rule)
if ruleType == "PROCESS-NAME" {
c.shouldFindProcess = true
}
r, err := c.parse(ruleType, rule, "", params)
if err != nil {
log.Warnln("parse rule error:[%s]", err.Error())
} else {
if r.ShouldResolveIP() {
c.shouldResolveIP = true
}
if r.ShouldFindProcess() {
if ruleType == "PROCESS-NAME" {
c.shouldFindProcess = true
}
c.rules = append(c.rules, r)
c.count++
}
}
r, err := c.parse(ruleType, rule, "", params)
if err != nil {
log.Warnln("parse rule error:[%s]", err.Error())
} else {
if !shouldResolveIP {
shouldResolveIP = r.ShouldResolveIP()
}
func (c *classicalStrategy) FinishInsert() {}
if !c.shouldFindProcess {
c.shouldFindProcess = r.ShouldFindProcess()
}
classicalRules = append(classicalRules, r)
}
}
c.rules = classicalRules
c.count = len(classicalRules)
}
func ruleParse(ruleRaw string) (string, string, []string) {
item := strings.Split(ruleRaw, ",")

View File

@ -3,13 +3,11 @@ package provider
import (
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
)
type domainStrategy struct {
count int
domainTrie *trie.DomainTrie[struct{}]
domainSet *trie.DomainSet
count int
domainRules *trie.DomainSet
}
func (d *domainStrategy) ShouldFindProcess() bool {
@ -17,7 +15,7 @@ func (d *domainStrategy) ShouldFindProcess() bool {
}
func (d *domainStrategy) Match(metadata *C.Metadata) bool {
return d.domainSet != nil && d.domainSet.Has(metadata.RuleHost())
return d.domainRules != nil && d.domainRules.Has(metadata.RuleHost())
}
func (d *domainStrategy) Count() int {
@ -28,24 +26,10 @@ func (d *domainStrategy) ShouldResolveIP() bool {
return false
}
func (d *domainStrategy) Reset() {
d.domainTrie = trie.New[struct{}]()
d.domainSet = nil
d.count = 0
}
func (d *domainStrategy) Insert(rule string) {
err := d.domainTrie.Insert(rule, struct{}{})
if err != nil {
log.Warnln("invalid domain:[%s]", rule)
} else {
d.count++
}
}
func (d *domainStrategy) FinishInsert() {
d.domainSet = d.domainTrie.NewDomainSet()
d.domainTrie = nil
func (d *domainStrategy) OnUpdate(rules []string) {
domainTrie := trie.NewDomainSet(rules)
d.domainRules = domainTrie
d.count = len(rules)
}
func NewDomainStrategy() *domainStrategy {

View File

@ -28,23 +28,22 @@ func (i *ipcidrStrategy) ShouldResolveIP() bool {
return i.shouldResolveIP
}
func (i *ipcidrStrategy) Reset() {
i.trie = trie.NewIpCidrTrie()
i.count = 0
i.shouldResolveIP = false
}
func (i *ipcidrStrategy) Insert(rule string) {
err := i.trie.AddIpCidrForString(rule)
if err != nil {
log.Warnln("invalid Ipcidr:[%s]", rule)
} else {
i.shouldResolveIP = true
i.count++
func (i *ipcidrStrategy) OnUpdate(rules []string) {
ipCidrTrie := trie.NewIpCidrTrie()
count := 0
for _, rule := range rules {
err := ipCidrTrie.AddIpCidrForString(rule)
if err != nil {
log.Warnln("invalid Ipcidr:[%s]", rule)
} else {
count++
}
}
}
func (i *ipcidrStrategy) FinishInsert() {}
i.trie = ipCidrTrie
i.count = count
i.shouldResolveIP = i.count > 0
}
func NewIPCidrStrategy() *ipcidrStrategy {
return &ipcidrStrategy{}

View File

@ -14,7 +14,6 @@ type ruleProviderSchema struct {
Behavior string `provider:"behavior"`
Path string `provider:"path"`
URL string `provider:"url,omitempty"`
Format string `provider:"format,omitempty"`
Interval int `provider:"interval,omitempty"`
}
@ -24,7 +23,7 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t
if err := decoder.Decode(mapping, schema); err != nil {
return nil, err
}
var behavior P.RuleBehavior
var behavior P.RuleType
switch schema.Behavior {
case "domain":
@ -37,17 +36,6 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t
return nil, fmt.Errorf("unsupported behavior type: %s", schema.Behavior)
}
var format P.RuleFormat
switch schema.Format {
case "", "yaml":
format = P.YamlRule
case "text":
format = P.TextRule
default:
return nil, fmt.Errorf("unsupported format type: %s", schema.Format)
}
path := C.Path.Resolve(schema.Path)
var vehicle P.Vehicle
switch schema.Type {
@ -59,5 +47,5 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t
return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type)
}
return NewRuleSetProvider(name, behavior, format, time.Duration(uint(schema.Interval))*time.Second, vehicle, parse), nil
return NewRuleSetProvider(name, behavior, time.Duration(uint(schema.Interval))*time.Second, vehicle, parse), nil
}

Some files were not shown because too many files have changed in this diff Show More