Compare commits

..

91 Commits

Author SHA1 Message Date
65071ea7d1 Merge branch 'Alpha' into Meta 2023-08-13 23:00:41 +08:00
fa94403629 chore: better resolv.conf parsing 2023-06-29 14:25:25 +08:00
1beb2919e7 fix: panic when add 4in6 ipcidr 2023-06-29 14:25:25 +08:00
75c5d0482e chore: better close single connection in restful api 2023-06-29 14:25:25 +08:00
55bcabdf46 chore: statistic's Snapshot only contains TrackerInfo 2023-06-29 14:25:25 +08:00
abf80601e1 chore: avoid unneeded map copy when close connection in restful api 2023-06-29 14:25:25 +08:00
26f97b45d6 chore: update quic-go to 0.36.0 2023-06-29 14:25:25 +08:00
29315ce8e5 fix: tuic server cwnd parsing 2023-06-29 14:25:25 +08:00
65f84d21ea chore: tuic server can handle V4 and V5 in same port 2023-06-29 14:25:25 +08:00
e3ac58bc51 chore: fix TUIC cwnd parsing 2023-06-29 14:25:25 +08:00
c66438d794 Revert "chore: Refine adapter type name"
This reverts commit 61734e5cac.
2023-06-29 14:25:25 +08:00
411e587460 chore: function rename 2023-06-29 14:25:25 +08:00
6cc9c68458 chore: Update dependencies 2023-06-29 14:25:25 +08:00
d1c858d7ff update docs 2023-06-29 14:25:25 +08:00
3eef1ee064 chore: adjustable cwnd for cc in quic 2023-06-29 14:25:25 +08:00
514d374b8c chore: Refine adapter type name 2023-06-29 14:25:25 +08:00
a2334430c1 feat: optional provider path (#624) 2023-06-29 14:25:25 +08:00
c8a3d6edd9 Add REALITY ChaCha20-Poly1305 auth mode support 2023-06-29 14:25:25 +08:00
bda2ca3c13 fix: singmux return wrong supportUDP value 2023-06-29 14:25:25 +08:00
f4b734c74c fix: tuicV5's heartbeat should be a datagram packet 2023-06-29 14:25:25 +08:00
c2cdf43239 fix: dns concurrent not work 2023-06-29 14:25:25 +08:00
b939c81d3e feat: support tuicV5 2023-06-29 14:25:25 +08:00
0e92496eeb chore: Update dependencies 2023-06-29 14:25:25 +08:00
ea482598e0 chore: Disable cache for RCode client 2023-06-29 14:25:25 +08:00
16f3567ddc feat: Add RCode DNS client 2023-06-29 14:25:25 +08:00
73f8da091e chore: allow unsafe path for provider by environment variable 2023-06-29 14:25:25 +08:00
6bdaadc581 chore: Replace murmur3 with maphash 2023-06-29 14:25:24 +08:00
73a2cf593e chore: reduce process lookup attempts when process not exist #613 2023-06-29 14:25:24 +08:00
665bfcab2d fix: Disable XUDP global ID if source address invalid 2023-06-29 14:25:24 +08:00
8be860472a chore: init gopacket only when dial fake-tcp to decrease memory using 2023-06-29 14:25:24 +08:00
1ec74f13f7 feat: Add XUDP migration support 2023-06-29 14:25:24 +08:00
564b834e00 fix: Resolve delay omission in the presence of nested proxy-groups 2023-06-29 14:25:24 +08:00
da04e00767 When testing the delay through REST API, determine whether to store the delay data based on certain conditions instead of discarding it directly (#609) 2023-06-29 14:25:24 +08:00
e0faffbfbd fix: go1.19 compile 2023-06-29 14:25:24 +08:00
a0c7641ad5 chore: Something update from clash :) (#606) 2023-06-29 14:25:24 +08:00
1f592c43de fix: nil pointer in urltest (#603) 2023-06-29 14:25:24 +08:00
4d7350923c fix hysteria faketcp lookback in TUN mode (#601) 2023-06-29 14:25:24 +08:00
76a7945994 chore: Ignore PR in Docker build 2023-06-29 14:25:24 +08:00
a2bbd1cc8d ProxyProvider health check also supports specifying expected status (#600)
Co-authored-by: wwqgtxx <wwqgtxx@gmail.com>
2023-06-29 14:25:24 +08:00
4ec66d299a [Feature] Proxy stores delay data of different URLs. And supports specifying different test URLs and expected statue by group (#588)
Co-authored-by: Larvan2 <78135608+Larvan2@users.noreply.github.com>
Co-authored-by: wwqgtxx <wwqgtxx@gmail.com>
2023-06-29 14:25:24 +08:00
4e46cbfbde fix: hysteria faketcp loopback in tun mode 2023-06-29 14:25:24 +08:00
1a44dcee55 chore: update proxy's udpConn when received a new packet 2023-06-29 14:25:24 +08:00
6c7d1657a5 chore: update quic-go to 0.35.1 2023-06-29 14:25:24 +08:00
38e210a851 chore: Update dependencies 2023-06-29 14:25:24 +08:00
359ee70daa chore: update quic-go to 0.34.0 2023-06-29 14:25:24 +08:00
8d1251f128 chore: generate release note automatically 2023-06-09 12:59:59 +00:00
fb6a032872 chore: genReleaseNote support verrsion range
This commit updates the release script to handle the input version range provided via command line options. Now, you can specify the version range using the '-v' option, such as '-v v1.14.1...v1.14.2', and the script will generate the release notes based on the specified range.

The script parses the command line options, extracts the version range, and uses it in the 'git log' commands to capture the relevant commits. The output is then organized into different sections, including 'What's Changed', 'BUG & Fix', and 'Maintenance', along with the full changelog link.

This enhancement improves the flexibility and usability of the release script, allowing users to generate release notes for specific version ranges easily.

Co-authored-by: ChatGPT
2023-06-03 10:25:00 +00:00
47ad8e08be chore: Random only if the certificate and private-key are empty 2023-06-03 10:02:31 +00:00
e1af4ddda3 chore: Reject packet conn implement wait read 2023-06-03 10:01:50 +00:00
58e05c42c9 fix: handle manually select in url-test 2023-06-03 09:55:29 +00:00
880cc90e10 Merge branch 'Alpha' into Meta 2023-06-03 09:54:25 +00:00
a4334e1d52 fix: wildcard matching problem (#536) 2023-04-29 01:10:55 +08:00
3ba94842cc chore: update genReleaseNote.sh 2023-04-28 07:29:58 +00:00
a266589faf Merge branch 'Alpha' into Meta 2023-04-28 07:05:21 +00:00
d9319ec09a chore: update bug_report 2023-04-27 12:19:59 +00:00
070f8f8949 Merge branch 'Alpha' into Meta 2023-04-26 14:14:46 +00:00
bf3c6a044c chore: update templates 2023-04-14 05:26:44 +00:00
d6d2d90502 add issue templates 2023-04-04 06:45:21 +00:00
a1d0f4c6ee chore: rename delete.yml 2023-03-31 06:34:23 +00:00
d569d8186d chore: add genReleaseNote.sh 2023-03-30 16:43:05 +00:00
9b7aab1fc7 chore: rename delete.yml 2023-03-30 16:04:09 +00:00
3c717097cb Merge branch 'Alpha' into Meta 2023-03-30 16:03:47 +00:00
8293b7fdae Merge branch 'Beta' into Meta
# Conflicts:
#	.github/workflows/docker.yaml
#	.github/workflows/prerelease.yml
2023-02-19 01:25:34 +08:00
0ba415866e Merge branch 'Alpha' into Beta 2023-02-19 01:25:01 +08:00
53b41ca166 Chore: Add action for deleting old workflow 2023-01-30 18:17:22 +08:00
8a75f78e63 chore: adjust Dockerfile 2023-01-12 02:24:12 +08:00
d9692c6366 Merge branch 'Beta' into Meta 2023-01-12 02:15:14 +08:00
f4b0062dfc Merge branch 'Alpha' into Beta 2023-01-12 02:14:49 +08:00
b9ffc82e53 Merge branch 'Beta' into Meta 2023-01-12 01:33:56 +08:00
78aaea6a45 Merge branch 'Alpha' into Beta 2023-01-12 01:33:16 +08:00
3645fbf161 Merge pull request #327 from Rasphino/Meta
Update flake.nix hash
2023-01-08 00:29:29 +08:00
a1d0f22132 fix: update flake.nix hash 2023-01-07 23:38:32 +08:00
fa73b0f4bf Merge remote-tracking branch 'origin/Beta' into Meta 2023-01-01 19:41:36 +08:00
3b76a8b839 Merge remote-tracking branch 'origin/Alpha' into Beta 2023-01-01 19:40:36 +08:00
667f42dcdc Merge pull request #282 from tdjnodj/Meta
Update README.md
2022-12-03 17:23:51 +08:00
dfbe09860f Update README.md 2022-12-03 17:17:08 +08:00
9e20f9c26a chore: update dependencies 2022-11-28 20:33:10 +08:00
f968d0cb82 chore: update github action 2022-11-26 20:16:12 +08:00
2ad84f4379 Merge branch 'Beta' into Meta 2022-11-02 18:08:22 +08:00
c7aa16426f Merge branch 'Alpha' into Beta 2022-11-02 18:07:29 +08:00
5987f8e3b5 Merge branch 'Beta' into Meta 2022-08-29 13:08:29 +08:00
3a8eb72de2 Merge branch 'Alpha' into Beta 2022-08-29 13:08:22 +08:00
33abbdfd24 Merge pull request #174 from MetaCubeX/Alpha
Alpha
2022-08-29 11:24:07 +08:00
0703d6cbff Merge pull request #173 from MetaCubeX/Alpha
Alpha
2022-08-29 11:22:14 +08:00
10d2d14938 Merge branch 'Beta' into Meta
# Conflicts:
#	rules/provider/classical_strategy.go
2022-07-02 10:41:41 +08:00
691cf1d8d6 Merge pull request #94 from bash99/Meta
Update README.md
2022-06-15 19:15:51 +08:00
d1decb8e58 Update README.md
add permissions for systemctl services
clash-dashboard change to updated one
2022-06-15 14:00:05 +08:00
7d04904109 fix: leak dns when domain in hosts list 2022-06-11 18:51:26 +08:00
a5acd3aa97 refactor: clear linkname,reduce cycle dependencies,transport init geosite function 2022-06-11 18:51:22 +08:00
eea9a12560 fix: 规则匹配默认策略组返回错误 2022-06-09 14:18:35 +08:00
0a4570b55c fix: group filter touch provider 2022-06-09 14:18:29 +08:00
92 changed files with 606 additions and 4182 deletions

82
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

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

@ -0,0 +1,36 @@
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
"

32
.github/genReleaseNote.sh vendored Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
while getopts "v:" opt; do
case $opt in
v)
version_range=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
if [ -z "$version_range" ]; then
echo "Please provide the version range using -v option. Example: ./genReleashNote.sh -v v1.14.1...v1.14.2"
exit 1
fi
echo "## What's Changed" > release.md
git log --pretty=format:"* %s by @%an" --grep="^feat" -i $version_range | sort -f | uniq >> release.md
echo "" >> release.md
echo "## BUG & Fix" >> release.md
git log --pretty=format:"* %s by @%an" --grep="^fix" -i $version_range | sort -f | uniq >> release.md
echo "" >> release.md
echo "## Maintenance" >> release.md
git log --pretty=format:"* %s by @%an" --grep="^chore\|^docs\|^refactor" -i $version_range | sort -f | uniq >> release.md
echo "" >> release.md
echo "**Full Changelog**: https://github.com/MetaCubeX/Clash.Meta/compare/$version_range" >> release.md

View File

@ -94,11 +94,6 @@ jobs:
run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
shell: bash
- name: Set variables
if: ${{github.ref_name=='Beta'}}
run: echo "VERSION=beta-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
shell: bash
- name: Set variables
if: ${{github.ref_name=='Meta'}}
run: echo "VERSION=meta-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
@ -267,6 +262,23 @@ jobs:
needs: [Build]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get tags
run: |
echo "CURRENTVERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
git fetch --tags
echo "PREVERSION=$(git describe --tags --abbrev=0 HEAD^)" >> $GITHUB_ENV
- name: Generate release notes
run: |
cp ./.github/genReleaseNote.sh ./
bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION}
rm ./genReleaseNote.sh
- uses: actions/download-artifact@v3
with:
name: artifact
@ -283,6 +295,7 @@ jobs:
tag_name: ${{ github.ref_name }}
files: bin/*
generate_release_notes: true
body_path: release.md
Docker:
if: ${{ github.event_name != 'pull_request' }}

15
.github/workflows/delete.yml vendored Normal file
View File

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

@ -18,8 +18,6 @@ import (
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/puzpuzpuz/xsync/v2"
)
var UnifiedDelay = atomic.NewBool(false)
@ -38,7 +36,7 @@ type Proxy struct {
history *queue.Queue[C.DelayHistory]
alive *atomic.Bool
url string
extra *xsync.MapOf[string, *extraProxyState]
extra map[string]*extraProxyState
}
// Alive implements C.Proxy
@ -48,8 +46,10 @@ func (p *Proxy) Alive() bool {
// AliveForTestUrl implements C.Proxy
func (p *Proxy) AliveForTestUrl(url string) bool {
if state, ok := p.extra.Load(url); ok {
return state.alive.Load()
if p.extra != nil {
if state, ok := p.extra[url]; ok {
return state.alive.Load()
}
}
return p.alive.Load()
@ -88,16 +88,16 @@ func (p *Proxy) DelayHistory() []C.DelayHistory {
for _, item := range queueM {
histories = append(histories, item)
}
return histories
}
// DelayHistoryForTestUrl implements C.Proxy
func (p *Proxy) DelayHistoryForTestUrl(url string) []C.DelayHistory {
var queueM []C.DelayHistory
if state, ok := p.extra.Load(url); ok {
queueM = state.history.Copy()
if p.extra != nil {
if state, ok := p.extra[url]; ok {
queueM = state.history.Copy()
}
}
if queueM == nil {
@ -112,25 +112,19 @@ func (p *Proxy) DelayHistoryForTestUrl(url string) []C.DelayHistory {
}
func (p *Proxy) ExtraDelayHistory() map[string][]C.DelayHistory {
extraHistory := map[string][]C.DelayHistory{}
extra := map[string][]C.DelayHistory{}
if p.extra != nil && len(p.extra) != 0 {
for testUrl, option := range p.extra {
histories := []C.DelayHistory{}
queueM := option.history.Copy()
for _, item := range queueM {
histories = append(histories, item)
}
p.extra.Range(func(k string, v *extraProxyState) bool {
testUrl := k
state := v
histories := []C.DelayHistory{}
queueM := state.history.Copy()
for _, item := range queueM {
histories = append(histories, item)
extra[testUrl] = histories
}
extraHistory[testUrl] = histories
return true
})
return extraHistory
}
return extra
}
// LastDelay return last history record. if proxy is not alive, return the max value of uint16.
@ -155,9 +149,11 @@ func (p *Proxy) LastDelayForTestUrl(url string) (delay uint16) {
alive := p.alive.Load()
history := p.history.Last()
if state, ok := p.extra.Load(url); ok {
alive = state.alive.Load()
history = state.history.Last()
if p.extra != nil {
if state, ok := p.extra[url]; ok {
alive = state.alive.Load()
history = state.history.Last()
}
}
if !alive {
@ -218,13 +214,17 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
record.Delay = t
}
state, ok := p.extra.Load(url)
if p.extra == nil {
p.extra = map[string]*extraProxyState{}
}
state, ok := p.extra[url]
if !ok {
state = &extraProxyState{
history: queue.New[C.DelayHistory](defaultHistoriesNum),
alive: atomic.NewBool(true),
}
p.extra.Store(url, state)
p.extra[url] = state
}
state.alive.Store(alive)
@ -307,12 +307,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
}
func NewProxy(adapter C.ProxyAdapter) *Proxy {
return &Proxy{
ProxyAdapter: adapter,
history: queue.New[C.DelayHistory](defaultHistoriesNum),
alive: atomic.NewBool(true),
url: "",
extra: xsync.NewMapOf[*extraProxyState]()}
return &Proxy{adapter, queue.New[C.DelayHistory](defaultHistoriesNum), atomic.NewBool(true), "", map[string]*extraProxyState{}}
}
func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
@ -355,14 +350,14 @@ func (p *Proxy) determineFinalStoreType(store C.DelayHistoryStoreType, url strin
return C.OriginalHistory
}
if p.extra.Size() < 2*C.DefaultMaxHealthCheckUrlNum {
return C.ExtraHistory
if p.extra == nil {
store = C.ExtraHistory
} else {
if _, ok := p.extra[url]; ok {
store = C.ExtraHistory
} else if len(p.extra) < 2*C.DefaultMaxHealthCheckUrlNum {
store = C.ExtraHistory
}
}
_, ok := p.extra.Load(url)
if ok {
return C.ExtraHistory
}
return store
}

View File

@ -1,22 +0,0 @@
package inbound
import (
"net"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/context"
"github.com/Dreamacro/clash/transport/socks5"
)
// NewMitm receive mitm request and return MitmContext
func NewMitm(target socks5.Addr, source net.Addr, userAgent string, conn net.Conn) *context.ConnContext {
metadata := parseSocksAddr(target)
metadata.NetWork = C.TCP
metadata.Type = C.MITM
metadata.UserAgent = userAgent
if ip, port, err := parseAddr(source); err == nil {
metadata.SrcIP = ip
metadata.SrcPort = port
}
return context.NewConnContext(conn, metadata)
}

View File

@ -3,8 +3,6 @@ package outbound
import (
"context"
"errors"
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"
@ -14,11 +12,6 @@ type Direct struct {
*Base
}
type DirectOption struct {
BasicOption
Name string `proxy:"name"`
}
// DialContext implements C.ProxyAdapter
func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
@ -26,7 +19,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
if err != nil {
return nil, err
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
return NewConn(c, d), nil
}
@ -47,21 +40,6 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
return newPacketConn(pc, d), nil
}
func NewDirectWithOption(option DirectOption) *Direct {
return &Direct{
Base: &Base{
name: option.Name,
tp: C.Direct,
udp: true,
tfo: option.TFO,
mpTcp: option.MPTCP,
iface: option.Interface,
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
}
}
func NewDirect() *Direct {
return &Direct{
Base: &Base{

View File

@ -7,13 +7,11 @@ import (
"encoding/base64"
"errors"
"fmt"
"io"
"net"
"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"
@ -76,7 +74,7 @@ func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metad
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", h.addr, err)
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
@ -113,10 +111,6 @@ func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
tempHeaders["Proxy-Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
}
if metadata.Type == C.MITM {
tempHeaders["Origin-Request-Source-Address"] = metadata.SourceAddress()
}
for key, value := range tempHeaders {
HeaderString += key + ": " + value + "\r\n"
}

View File

@ -1,50 +0,0 @@
package outbound
import (
"context"
"net"
"time"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
)
type Mitm struct {
*Base
serverAddr *net.TCPAddr
httpProxyClient *Http
}
// DialContext implements C.ProxyAdapter
func (m *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...dialer.Option) (C.Conn, error) {
c, err := net.DialTCP("tcp", nil, m.serverAddr)
if err != nil {
return nil, err
}
_ = c.SetKeepAlive(true)
_ = c.SetKeepAlivePeriod(60 * time.Second)
metadata.Type = C.MITM
hc, err := m.httpProxyClient.StreamConnContext(ctx, c, metadata)
if err != nil {
_ = c.Close()
return nil, err
}
return NewConn(hc, m), nil
}
func NewMitm(serverAddr string) *Mitm {
tcpAddr, _ := net.ResolveTCPAddr("tcp", serverAddr)
http, _ := NewHttp(HttpOption{})
return &Mitm{
Base: &Base{
name: "Mitm",
tp: C.Mitm,
},
serverAddr: tcpAddr,
httpProxyClient: http,
}
}

View File

@ -19,7 +19,7 @@ import (
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
restlsC "github.com/3andne/restls-client-go"
shadowsocks "github.com/metacubex/sing-shadowsocks2"
"github.com/metacubex/sing-shadowsocks2"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/uot"
)
@ -146,7 +146,7 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
@ -294,6 +294,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
}
restlsConfig, err = restlsC.NewRestlsConfig(restlsOpt.Host, restlsOpt.Password, restlsOpt.VersionHint, restlsOpt.RestlsScript, option.ClientFingerprint)
restlsConfig.SessionTicketsDisabled = true
if err != nil {
return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err)
}

View File

@ -80,7 +80,7 @@ func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dia
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err)
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)

View File

@ -6,7 +6,6 @@ import (
"net"
"strconv"
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"
@ -94,7 +93,7 @@ func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", s.addr, err)
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
@ -122,7 +121,7 @@ func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
if err != nil {
return nil, err
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption})
err = snell.WriteUDPHeader(c, s.version)
@ -208,7 +207,7 @@ func NewSnell(option SnellOption) (*Snell, error) {
return nil, err
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
return streamConn(c, streamOption{psk, option.Version, addr, obfsOption}), nil
})
}

View File

@ -9,7 +9,6 @@ import (
"net"
"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"
@ -81,7 +80,7 @@ func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, me
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
@ -127,7 +126,7 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
safeConnClose(c, err)
}(c)
N.TCPKeepAlive(c)
tcpKeepAlive(c)
var user *socks5.User
if ss.user != "" {
user = &socks5.User{

View File

@ -8,7 +8,6 @@ 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"
@ -132,7 +131,7 @@ func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, met
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
@ -185,7 +184,7 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me
defer func(c net.Conn) {
safeConnClose(c, err)
}(c)
N.TCPKeepAlive(c)
tcpKeepAlive(c)
c, err = t.plainStream(ctx, c)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
@ -269,7 +268,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error())
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
return c, nil
}

View File

@ -6,7 +6,6 @@ import (
"crypto/tls"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"math"
"net"
@ -16,15 +15,12 @@ import (
"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"
"github.com/Dreamacro/clash/transport/tuic"
"github.com/gofrs/uuid/v5"
"github.com/metacubex/quic-go"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/uot"
)
type Tuic struct {
@ -63,9 +59,6 @@ type TuicOption struct {
DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"`
MaxDatagramFrameSize int `proxy:"max-datagram-frame-size,omitempty"`
SNI string `proxy:"sni,omitempty"`
UDPOverStream bool `proxy:"udp-over-stream,omitempty"`
UDPOverStreamVersion int `proxy:"udp-over-stream-version,omitempty"`
}
// DialContext implements C.ProxyAdapter
@ -89,32 +82,6 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op
// ListenPacketWithDialer implements C.ProxyAdapter
func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
if t.option.UDPOverStream {
uotDestination := uot.RequestDestination(uint8(t.option.UDPOverStreamVersion))
uotMetadata := *metadata
uotMetadata.Host = uotDestination.Fqdn
uotMetadata.DstPort = uotDestination.Port
c, err := t.DialContextWithDialer(ctx, dialer, &uotMetadata)
if err != nil {
return nil, err
}
// tuic uos use stream-oriented udp with a special address, so we need a net.UDPAddr
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(ctx, metadata.Host)
if err != nil {
return nil, errors.New("can't resolve ip")
}
metadata.DstIP = ip
}
destination := M.SocksaddrFromNet(metadata.UDPAddr())
if t.option.UDPOverStreamVersion == uot.LegacyVersion {
return newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), t), nil
} else {
return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), t), nil
}
}
pc, err := t.client.ListenPacketWithDialer(ctx, metadata, dialer, t.dialWithDialer)
if err != nil {
return nil, err
@ -272,14 +239,6 @@ func NewTuic(option TuicOption) (*Tuic, error) {
tlsConfig.InsecureSkipVerify = true // tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config
}
switch option.UDPOverStreamVersion {
case uot.Version, uot.LegacyVersion:
case 0:
option.UDPOverStreamVersion = uot.LegacyVersion
default:
return nil, fmt.Errorf("tuic %s unknown udp over stream protocol version: %d", addr, option.UDPOverStreamVersion)
}
t := &Tuic{
Base: &Base{
name: option.Name,

View File

@ -7,6 +7,7 @@ import (
"net"
"net/netip"
"sync"
"time"
"github.com/Dreamacro/clash/component/resolver"
C "github.com/Dreamacro/clash/constant"
@ -18,6 +19,13 @@ var (
once sync.Once
)
func tcpKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
_ = tcp.SetKeepAlive(true)
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
}
}
func getClientSessionCache() tls.ClientSessionCache {
once.Do(func() {
globalClientSessionCache = tls.NewLRUClientSessionCache(128)

View File

@ -57,7 +57,6 @@ type VlessOption struct {
UUID string `proxy:"uuid"`
Flow string `proxy:"flow,omitempty"`
TLS bool `proxy:"tls,omitempty"`
ALPN []string `proxy:"alpn,omitempty"`
UDP bool `proxy:"udp,omitempty"`
PacketAddr bool `proxy:"packet-addr,omitempty"`
XUDP bool `proxy:"xudp,omitempty"`
@ -212,7 +211,6 @@ func (v *Vless) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (ne
FingerPrint: v.option.Fingerprint,
ClientFingerprint: v.option.ClientFingerprint,
Reality: v.realityConfig,
NextProtos: v.option.ALPN,
}
if isH2 {
@ -263,7 +261,7 @@ func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
}(c)
@ -328,7 +326,7 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
}(c)
@ -578,7 +576,7 @@ func NewVless(option VlessOption) (*Vless, error) {
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
return c, nil
}

View File

@ -18,7 +18,6 @@ import (
"github.com/Dreamacro/clash/component/resolver"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/ntp"
"github.com/Dreamacro/clash/transport/gun"
clashVMess "github.com/Dreamacro/clash/transport/vmess"
@ -53,7 +52,6 @@ type VmessOption struct {
UDP bool `proxy:"udp,omitempty"`
Network string `proxy:"network,omitempty"`
TLS bool `proxy:"tls,omitempty"`
ALPN []string `proxy:"alpn,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
ServerName string `proxy:"servername,omitempty"`
@ -151,7 +149,6 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
SkipCertVerify: v.option.SkipCertVerify,
ClientFingerprint: v.option.ClientFingerprint,
Reality: v.realityConfig,
NextProtos: v.option.ALPN,
}
if v.option.ServerName != "" {
@ -208,7 +205,6 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
SkipCertVerify: v.option.SkipCertVerify,
ClientFingerprint: v.option.ClientFingerprint,
Reality: v.realityConfig,
NextProtos: v.option.ALPN,
}
if v.option.ServerName != "" {
@ -308,7 +304,7 @@ func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
}(c)
@ -369,7 +365,7 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
defer func(c net.Conn) {
safeConnClose(c, err)
}(c)
@ -417,7 +413,6 @@ func NewVmess(option VmessOption) (*Vmess, error) {
if option.AuthenticatedLength {
options = append(options, vmess.ClientWithAuthenticatedLength())
}
options = append(options, vmess.ClientWithTimeFunc(ntp.Now))
client, err := vmess.NewClient(option.UUID, security, option.AlterID, options...)
if err != nil {
return nil, err
@ -469,7 +464,7 @@ func NewVmess(option VmessOption) (*Vmess, error) {
if err != nil {
return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error())
}
N.TCPKeepAlive(c)
tcpKeepAlive(c)
return c, nil
}

View File

@ -302,7 +302,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
if err != nil {
return nil, E.Cause(err, "create WireGuard device")
}
outbound.device = device.NewDevice(context.Background(), outbound.tunDevice, outbound.bind, &device.Logger{
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...)))
},

View File

@ -1,5 +1,17 @@
package outboundgroup
import (
"net"
"time"
)
func tcpKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
_ = tcp.SetKeepAlive(true)
_ = tcp.SetKeepAlivePeriod(30 * time.Second)
}
}
type SelectAble interface {
Set(string) error
ForceSet(name string)

View File

@ -106,13 +106,6 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
break
}
proxy, err = outbound.NewTuic(*tuicOption)
case "direct":
directOption := &outbound.DirectOption{}
err = decoder.Decode(mapping, directOption)
if err != nil {
break
}
proxy = outbound.NewDirectWithOption(*directOption)
default:
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
}

View File

@ -14,6 +14,13 @@ var NewSize = buf.NewSize
var With = buf.With
var As = buf.As
var KeepAlive = common.KeepAlive
//go:norace
func Dup[T any](obj T) T {
return common.Dup(obj)
}
var (
Must = common.Must
Error = common.Error

View File

@ -7,8 +7,6 @@ import (
"time"
"github.com/Dreamacro/clash/common/generics/list"
"github.com/samber/lo"
)
// Option is part of Functional Options Pattern
@ -89,7 +87,7 @@ func (c *LruCache[K, V]) Get(key K) (V, bool) {
el := c.get(key)
if el == nil {
return lo.Empty[V](), false
return getZero[V](), false
}
value := el.value
@ -121,7 +119,7 @@ func (c *LruCache[K, V]) GetWithExpire(key K) (V, time.Time, bool) {
el := c.get(key)
if el == nil {
return lo.Empty[V](), time.Time{}, false
return getZero[V](), time.Time{}, false
}
return el.value, time.Unix(el.expires, 0), true
@ -261,3 +259,8 @@ type entry[K comparable, V any] struct {
value V
expires int64
}
func getZero[T any]() T {
var result T
return result
}

View File

@ -1,303 +0,0 @@
package cert
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"os"
"strings"
"sync/atomic"
"time"
)
var currentSerialNumber = time.Now().Unix()
type Config struct {
ca *x509.Certificate
caPrivateKey *rsa.PrivateKey
roots *x509.CertPool
privateKey *rsa.PrivateKey
validity time.Duration
keyID []byte
organization string
certsStorage CertsStorage
}
type CertsStorage interface {
Get(key string) (*tls.Certificate, bool)
Set(key string, cert *tls.Certificate)
}
func NewAuthority(name, organization string, validity time.Duration) (*x509.Certificate, *rsa.PrivateKey, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
pub := privateKey.Public()
pkixPub, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, nil, err
}
h := sha1.New()
_, err = h.Write(pkixPub)
if err != nil {
return nil, nil, err
}
keyID := h.Sum(nil)
serial := atomic.AddInt64(&currentSerialNumber, 1)
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(serial),
Subject: pkix.Name{
CommonName: name,
Organization: []string{organization},
},
SubjectKeyId: keyID,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
NotBefore: time.Now().Add(-validity),
NotAfter: time.Now().Add(validity),
DNSNames: []string{name},
IsCA: true,
}
raw, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, pub, privateKey)
if err != nil {
return nil, nil, err
}
x509c, err := x509.ParseCertificate(raw)
if err != nil {
return nil, nil, err
}
return x509c, privateKey, nil
}
func NewConfig(ca *x509.Certificate, caPrivateKey *rsa.PrivateKey) (*Config, error) {
roots := x509.NewCertPool()
roots.AddCert(ca)
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
pub := privateKey.Public()
pkixPub, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, err
}
h := sha1.New()
_, err = h.Write(pkixPub)
if err != nil {
return nil, err
}
keyID := h.Sum(nil)
return &Config{
ca: ca,
caPrivateKey: caPrivateKey,
privateKey: privateKey,
keyID: keyID,
validity: time.Hour,
organization: "Clash",
certsStorage: NewDomainTrieCertsStorage(),
roots: roots,
}, nil
}
func (c *Config) GetCA() *x509.Certificate {
return c.ca
}
func (c *Config) SetOrganization(organization string) {
c.organization = organization
}
func (c *Config) SetValidity(validity time.Duration) {
c.validity = validity
}
func (c *Config) NewTLSConfigForHost(hostname string) *tls.Config {
tlsConfig := &tls.Config{
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
host := clientHello.ServerName
if host == "" {
host = hostname
}
return c.GetOrCreateCert(host)
},
NextProtos: []string{"http/1.1"},
}
tlsConfig.InsecureSkipVerify = true
return tlsConfig
}
func (c *Config) GetOrCreateCert(hostname string, ips ...net.IP) (*tls.Certificate, error) {
var leaf *x509.Certificate
tlsCertificate, ok := c.certsStorage.Get(hostname)
if ok {
leaf = tlsCertificate.Leaf
if _, err := leaf.Verify(x509.VerifyOptions{
DNSName: hostname,
Roots: c.roots,
}); err == nil {
return tlsCertificate, nil
}
}
var (
key = hostname
topHost = hostname
wildcardHost = "*." + hostname
dnsNames []string
)
if ip := net.ParseIP(hostname); ip != nil {
ips = append(ips, ip)
} else {
parts := strings.Split(hostname, ".")
l := len(parts)
if leaf != nil {
dnsNames = append(dnsNames, leaf.DNSNames...)
}
if l > 2 {
topIndex := l - 2
topHost = strings.Join(parts[topIndex:], ".")
for i := topIndex; i > 0; i-- {
wildcardHost = "*." + strings.Join(parts[i:], ".")
if i == topIndex && (len(dnsNames) == 0 || dnsNames[0] != topHost) {
dnsNames = append(dnsNames, topHost, wildcardHost)
} else if !hasDnsNames(dnsNames, wildcardHost) {
dnsNames = append(dnsNames, wildcardHost)
}
}
} else {
dnsNames = append(dnsNames, topHost, wildcardHost)
}
key = "+." + topHost
}
serial := atomic.AddInt64(&currentSerialNumber, 1)
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(serial),
Subject: pkix.Name{
CommonName: topHost,
Organization: []string{c.organization},
},
SubjectKeyId: c.keyID,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
NotBefore: time.Now().Add(-c.validity),
NotAfter: time.Now().Add(c.validity),
DNSNames: dnsNames,
IPAddresses: ips,
}
raw, err := x509.CreateCertificate(rand.Reader, tmpl, c.ca, c.privateKey.Public(), c.caPrivateKey)
if err != nil {
return nil, err
}
x509c, err := x509.ParseCertificate(raw)
if err != nil {
return nil, err
}
tlsCertificate = &tls.Certificate{
Certificate: [][]byte{raw, c.ca.Raw},
PrivateKey: c.privateKey,
Leaf: x509c,
}
c.certsStorage.Set(key, tlsCertificate)
return tlsCertificate, nil
}
// GenerateAndSave generate CA private key and CA certificate and dump them to file
func GenerateAndSave(caPath string, caKeyPath string) error {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return err
}
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(time.Now().Unix()),
Subject: pkix.Name{
Country: []string{"US"},
CommonName: "Clash Root CA",
Organization: []string{"Clash Trust Services"},
},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
NotBefore: time.Now().Add(-(time.Hour * 24 * 60)),
NotAfter: time.Now().Add(time.Hour * 24 * 365 * 25),
BasicConstraintsValid: true,
IsCA: true,
}
caRaw, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, privateKey.Public(), privateKey)
if err != nil {
return err
}
caOut, err := os.OpenFile(caPath, os.O_CREATE|os.O_WRONLY, 0o600)
if err != nil {
return err
}
defer func(caOut *os.File) {
_ = caOut.Close()
}(caOut)
if err = pem.Encode(caOut, &pem.Block{Type: "CERTIFICATE", Bytes: caRaw}); err != nil {
return err
}
caKeyOut, err := os.OpenFile(caKeyPath, os.O_CREATE|os.O_WRONLY, 0o600)
if err != nil {
return err
}
defer func(caKeyOut *os.File) {
_ = caKeyOut.Close()
}(caKeyOut)
if err = pem.Encode(caKeyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}); err != nil {
return err
}
return nil
}
func hasDnsNames(dnsNames []string, hostname string) bool {
for _, name := range dnsNames {
if name == hostname {
return true
}
}
return false
}

View File

@ -1,32 +0,0 @@
package cert
import (
"crypto/tls"
"github.com/Dreamacro/clash/component/trie"
)
// DomainTrieCertsStorage cache wildcard certificates
type DomainTrieCertsStorage struct {
certsCache *trie.DomainTrie[*tls.Certificate]
}
// Get gets the certificate from the storage
func (c *DomainTrieCertsStorage) Get(key string) (*tls.Certificate, bool) {
ca := c.certsCache.Search(key)
if ca == nil {
return nil, false
}
return ca.Data(), true
}
// Set saves the certificate to the storage
func (c *DomainTrieCertsStorage) Set(key string, cert *tls.Certificate) {
_ = c.certsCache.Insert(key, cert)
}
func NewDomainTrieCertsStorage() *DomainTrieCertsStorage {
return &DomainTrieCertsStorage{
certsCache: trie.New[*tls.Certificate](),
}
}

View File

@ -21,7 +21,7 @@ func TestSplitArgs(t *testing.T) {
func TestExecCmd(t *testing.T) {
if runtime.GOOS == "windows" {
_, err := ExecCmd("cmd -c 'dir'")
_, err := ExecCmd("dir")
assert.Nil(t, err)
return
}

View File

@ -50,9 +50,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
hysteria["port"] = urlHysteria.Port()
hysteria["sni"] = query.Get("peer")
hysteria["obfs"] = query.Get("obfs")
if alpn := query.Get("alpn"); alpn != "" {
hysteria["alpn"] = strings.Split(alpn, ",")
}
hysteria["alpn"] = []string{query.Get("alpn")}
hysteria["auth_str"] = query.Get("auth")
hysteria["protocol"] = query.Get("protocol")
up := query.Get("up")
@ -69,47 +67,6 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
proxies = append(proxies, hysteria)
case "tuic":
// A temporary unofficial TUIC share link standard
// Modified from https://github.com/daeuniverse/dae/discussions/182
// Changes:
// 1. Support TUICv4, just replace uuid:password with token
// 2. Remove `allow_insecure` field
urlTUIC, err := url.Parse(line)
if err != nil {
continue
}
query := urlTUIC.Query()
tuic := make(map[string]any, 20)
tuic["name"] = uniqueName(names, urlTUIC.Fragment)
tuic["type"] = scheme
tuic["server"] = urlTUIC.Hostname()
tuic["port"] = urlTUIC.Port()
tuic["udp"] = true
password, v5 := urlTUIC.User.Password()
if v5 {
tuic["uuid"] = urlTUIC.User.Username()
tuic["password"] = password
} else {
tuic["token"] = urlTUIC.User.Username()
}
if cc := query.Get("congestion_control"); cc != "" {
tuic["congestion-controller"] = cc
}
if alpn := query.Get("alpn"); alpn != "" {
tuic["alpn"] = strings.Split(alpn, ",")
}
if sni := query.Get("sni"); sni != "" {
tuic["sni"] = sni
}
if query.Get("disable_sni") == "1" {
tuic["disable-sni"] = true
}
if udpRelayMode := query.Get("udp_relay_mode"); udpRelayMode != "" {
tuic["udp-relay-mode"] = udpRelayMode
}
case "trojan":
urlTrojan, err := url.Parse(line)
if err != nil {
@ -129,12 +86,10 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
trojan["udp"] = true
trojan["skip-cert-verify"], _ = strconv.ParseBool(query.Get("allowInsecure"))
if sni := query.Get("sni"); sni != "" {
sni := query.Get("sni")
if sni != "" {
trojan["sni"] = sni
}
if alpn := query.Get("alpn"); alpn != "" {
trojan["alpn"] = strings.Split(alpn, ",")
}
network := strings.ToLower(query.Get("type"))
if network != "" {
@ -262,9 +217,6 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
if strings.HasSuffix(tls, "tls") {
vmess["tls"] = true
}
if alpn, ok := values["alpn"].(string); ok {
vmess["alpn"] = strings.Split(alpn, ",")
}
}
switch network {
@ -380,7 +332,6 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
}
}
proxies = append(proxies, ss)
case "ssr":
dcBuf, err := encRaw.DecodeString(body)
if err != nil {

View File

@ -24,6 +24,8 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m
proxy["port"] = url.Port()
proxy["uuid"] = url.User.Username()
proxy["udp"] = true
proxy["skip-cert-verify"] = false
proxy["tls"] = false
tls := strings.ToLower(query.Get("security"))
if strings.HasSuffix(tls, "tls") || tls == "reality" {
proxy["tls"] = true
@ -32,9 +34,6 @@ func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy m
} else {
proxy["client-fingerprint"] = fingerprint
}
if alpn := query.Get("alpn"); alpn != "" {
proxy["alpn"] = strings.Split(alpn, ",")
}
}
if sni := query.Get("sni"); sni != "" {
proxy["servername"] = sni

View File

@ -4,11 +4,8 @@ import (
"fmt"
"net"
"strings"
"time"
)
var KeepAliveInterval time.Duration
func SplitNetworkType(s string) (string, string, error) {
var (
shecme string
@ -47,10 +44,3 @@ func SplitHostPort(s string) (host, port string, hasPort bool, err error) {
host, port, err = net.SplitHostPort(temp)
return
}
func TCPKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
_ = tcp.SetKeepAlive(true)
_ = tcp.SetKeepAlivePeriod(KeepAliveInterval * time.Second)
}
}

View File

@ -5,7 +5,6 @@ import (
"testing"
"time"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
)
@ -16,7 +15,7 @@ func sleepAndSend[T any](ctx context.Context, delay int, input T) func() (T, err
case <-timer.C:
return input, nil
case <-ctx.Done():
return lo.Empty[T](), ctx.Err()
return getZero[T](), ctx.Err()
}
}
}
@ -36,6 +35,11 @@ func TestPicker_Timeout(t *testing.T) {
picker.Go(sleepAndSend(ctx, 20, 1))
number := picker.Wait()
assert.Equal(t, number, lo.Empty[int]())
assert.Equal(t, number, getZero[int]())
assert.NotNil(t, picker.Error())
}
func getZero[T any]() T {
var result T
return result
}

View File

@ -2,8 +2,6 @@ package queue
import (
"sync"
"github.com/samber/lo"
)
// Queue is a simple concurrent safe queue
@ -26,7 +24,7 @@ func (q *Queue[T]) Put(items ...T) {
// Pop returns the head of items.
func (q *Queue[T]) Pop() T {
if len(q.items) == 0 {
return lo.Empty[T]()
return GetZero[T]()
}
q.lock.Lock()
@ -39,7 +37,7 @@ func (q *Queue[T]) Pop() T {
// Last returns the last of item.
func (q *Queue[T]) Last() T {
if len(q.items) == 0 {
return lo.Empty[T]()
return GetZero[T]()
}
q.lock.RLock()
@ -71,3 +69,8 @@ func New[T any](hint int64) *Queue[T] {
items: make([]T, 0, hint),
}
}
func GetZero[T any]() T {
var result T
return result
}

View File

@ -1,7 +1,7 @@
package auth
import (
"github.com/puzpuzpuz/xsync/v2"
"sync"
)
type Authenticator interface {
@ -15,7 +15,7 @@ type AuthUser struct {
}
type inMemoryAuthenticator struct {
storage *xsync.MapOf[string, string]
storage *sync.Map
usernames []string
}
@ -31,13 +31,13 @@ func NewAuthenticator(users []AuthUser) Authenticator {
return nil
}
au := &inMemoryAuthenticator{storage: xsync.NewMapOf[string]()}
au := &inMemoryAuthenticator{storage: &sync.Map{}}
for _, user := range users {
au.storage.Store(user.User, user.Pass)
}
usernames := make([]string, 0, len(users))
au.storage.Range(func(key string, value string) bool {
usernames = append(usernames, key)
au.storage.Range(func(key, value any) bool {
usernames = append(usernames, key.(string))
return true
})
au.usernames = usernames

View File

@ -162,22 +162,14 @@ 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)
if len(ipv4s) == 0 && len(ipv6s) == 0 {
return nil, ErrorNoIpAddress
}
preferIPVersion := opt.prefer
fallbackTicker := time.NewTicker(fallbackTimeout)
defer fallbackTicker.Stop()
results := make(chan dialResult)
returned := make(chan struct{})
defer close(returned)
var wg sync.WaitGroup
racer := func(ips []netip.Addr, isPrimary bool) {
defer wg.Done()
result := dialResult{isPrimary: isPrimary}
defer func() {
select {
@ -190,36 +182,18 @@ func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string,
}()
result.Conn, result.error = dialFn(ctx, network, ips, port, opt)
}
if len(ipv4s) != 0 {
wg.Add(1)
go racer(ipv4s, preferIPVersion != 6)
}
if len(ipv6s) != 0 {
wg.Add(1)
go racer(ipv6s, preferIPVersion != 4)
}
go func() {
wg.Wait()
close(results)
}()
go racer(ipv4s, preferIPVersion != 6)
go racer(ipv6s, preferIPVersion != 4)
var fallback dialResult
var errs []error
loop:
for {
for i := 0; i < 2; {
select {
case <-fallbackTicker.C:
if fallback.error == nil && fallback.Conn != nil {
return fallback.Conn, nil
}
case res, ok := <-results:
if !ok {
break loop
}
case res := <-results:
i++
if res.error == nil {
if res.isPrimary {
return res.Conn, nil
@ -234,7 +208,6 @@ loop:
}
}
}
if fallback.error == nil && fallback.Conn != nil {
return fallback.Conn, nil
}

View File

@ -32,7 +32,7 @@ func (g GeoIPCache) Set(key string, value *router.GeoIP) {
}
func (g GeoIPCache) Unmarshal(filename, code string) (*router.GeoIP, error) {
asset := C.Path.Resolve(filename)
asset := C.Path.GetAssetLocation(filename)
idx := strings.ToLower(asset + ":" + code)
if g.Has(idx) {
return g.Get(idx), nil
@ -97,7 +97,7 @@ func (g GeoSiteCache) Set(key string, value *router.GeoSite) {
}
func (g GeoSiteCache) Unmarshal(filename, code string) (*router.GeoSite, error) {
asset := C.Path.Resolve(filename)
asset := C.Path.GetAssetLocation(filename)
idx := strings.ToLower(asset + ":" + code)
if g.Has(idx) {
return g.Get(idx), nil

View File

@ -26,7 +26,7 @@ func ReadFile(path string) ([]byte, error) {
}
func ReadAsset(file string) ([]byte, error) {
return ReadFile(C.Path.Resolve(file))
return ReadFile(C.Path.GetAssetLocation(file))
}
func loadIP(geoipBytes []byte, country string) ([]*router.CIDR, error) {

View File

@ -5,28 +5,23 @@ import (
"sync"
C "github.com/Dreamacro/clash/constant"
"github.com/puzpuzpuz/xsync/v2"
)
type Table struct {
mapping *xsync.MapOf[string, *Entry]
lockMap *xsync.MapOf[string, *sync.Cond]
mapping sync.Map
}
type Entry struct {
PacketConn C.PacketConn
WriteBackProxy C.WriteBackProxy
LocalUDPConnMap *xsync.MapOf[string, *net.UDPConn]
LocalLockMap *xsync.MapOf[string, *sync.Cond]
LocalUDPConnMap sync.Map
}
func (t *Table) Set(key string, e C.PacketConn, w C.WriteBackProxy) {
t.mapping.Store(key, &Entry{
PacketConn: e,
WriteBackProxy: w,
LocalUDPConnMap: xsync.NewMapOf[*net.UDPConn](),
LocalLockMap: xsync.NewMapOf[*sync.Cond](),
LocalUDPConnMap: sync.Map{},
})
}
@ -39,19 +34,15 @@ func (t *Table) Get(key string) (C.PacketConn, C.WriteBackProxy) {
}
func (t *Table) GetOrCreateLock(key string) (*sync.Cond, bool) {
item, loaded := t.lockMap.LoadOrCompute(key, makeLock)
return item, loaded
item, loaded := t.mapping.LoadOrStore(key, sync.NewCond(&sync.Mutex{}))
return item.(*sync.Cond), loaded
}
func (t *Table) Delete(key string) {
t.mapping.Delete(key)
}
func (t *Table) DeleteLock(lockKey string) {
t.lockMap.Delete(lockKey)
}
func (t *Table) GetForLocalConn(lAddr, rAddr string) *net.UDPConn {
func (t *Table) GetLocalConn(lAddr, rAddr string) *net.UDPConn {
entry, exist := t.getEntry(lAddr)
if !exist {
return nil
@ -60,10 +51,10 @@ func (t *Table) GetForLocalConn(lAddr, rAddr string) *net.UDPConn {
if !exist {
return nil
}
return item
return item.(*net.UDPConn)
}
func (t *Table) AddForLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool {
func (t *Table) AddLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool {
entry, exist := t.getEntry(lAddr)
if !exist {
return false
@ -72,7 +63,7 @@ func (t *Table) AddForLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool {
return true
}
func (t *Table) RangeForLocalConn(lAddr string, f func(key string, value *net.UDPConn) bool) {
func (t *Table) RangeLocalConn(lAddr string, f func(key, value any) bool) {
entry, exist := t.getEntry(lAddr)
if !exist {
return
@ -85,11 +76,11 @@ func (t *Table) GetOrCreateLockForLocalConn(lAddr, key string) (*sync.Cond, bool
if !loaded {
return nil, false
}
item, loaded := entry.LocalLockMap.LoadOrCompute(key, makeLock)
return item, loaded
item, loaded := entry.LocalUDPConnMap.LoadOrStore(key, sync.NewCond(&sync.Mutex{}))
return item.(*sync.Cond), loaded
}
func (t *Table) DeleteForLocalConn(lAddr, key string) {
func (t *Table) DeleteLocalConnMap(lAddr, key string) {
entry, loaded := t.getEntry(lAddr)
if !loaded {
return
@ -97,26 +88,17 @@ func (t *Table) DeleteForLocalConn(lAddr, key string) {
entry.LocalUDPConnMap.Delete(key)
}
func (t *Table) DeleteLockForLocalConn(lAddr, key string) {
entry, loaded := t.getEntry(lAddr)
if !loaded {
return
}
entry.LocalLockMap.Delete(key)
}
func (t *Table) getEntry(key string) (*Entry, bool) {
return t.mapping.Load(key)
}
func makeLock() *sync.Cond {
return sync.NewCond(&sync.Mutex{})
item, ok := t.mapping.Load(key)
// This should not happen usually since this function called after PacketConn created
if !ok {
return nil, false
}
entry, ok := item.(*Entry)
return entry, ok
}
// New return *Cache
func New() *Table {
return &Table{
mapping: xsync.NewMapOf[*Entry](),
lockMap: xsync.NewMapOf[*sync.Cond](),
}
return &Table{}
}

View File

@ -9,8 +9,6 @@ import (
types "github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/log"
"github.com/samber/lo"
)
var (
@ -67,7 +65,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
}
if err != nil {
return lo.Empty[V](), err
return getZero[V](), err
}
var contents V
@ -87,18 +85,18 @@ func (f *Fetcher[V]) Initial() (V, error) {
if err != nil {
if !isLocal {
return lo.Empty[V](), err
return getZero[V](), err
}
// parse local file error, fallback to remote
buf, err = f.vehicle.Read()
if err != nil {
return lo.Empty[V](), err
return getZero[V](), err
}
contents, err = f.parser(buf)
if err != nil {
return lo.Empty[V](), err
return getZero[V](), err
}
isLocal = false
@ -106,7 +104,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
if f.vehicle.Type() != types.File && !isLocal {
if err := safeWrite(f.vehicle.Path(), buf); err != nil {
return lo.Empty[V](), err
return getZero[V](), err
}
}
@ -123,7 +121,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
func (f *Fetcher[V]) Update() (V, bool, error) {
buf, err := f.vehicle.Read()
if err != nil {
return lo.Empty[V](), false, err
return getZero[V](), false, err
}
now := time.Now()
@ -131,17 +129,17 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
if bytes.Equal(f.hash[:], hash[:]) {
f.UpdatedAt = &now
_ = os.Chtimes(f.vehicle.Path(), now, now)
return lo.Empty[V](), true, nil
return getZero[V](), true, nil
}
contents, err := f.parser(buf)
if err != nil {
return lo.Empty[V](), false, err
return getZero[V](), false, err
}
if f.vehicle.Type() != types.File {
if err := safeWrite(f.vehicle.Path(), buf); err != nil {
return lo.Empty[V](), false, err
return getZero[V](), false, err
}
}
@ -212,3 +210,8 @@ func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicl
interval: interval,
}
}
func getZero[V any]() V {
var result V
return result
}

View File

@ -22,7 +22,6 @@ import (
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/ntp"
utls "github.com/sagernet/utls"
"github.com/zhangyunhao116/fastrand"
@ -71,7 +70,7 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string
rawSessionID[i] = 0
}
binary.BigEndian.PutUint64(hello.SessionId, uint64(ntp.Now().Unix()))
binary.BigEndian.PutUint64(hello.SessionId, uint64(time.Now().Unix()))
copy(hello.SessionId[8:], realityConfig.ShortID[:])
hello.SessionId[0] = 1

View File

@ -16,7 +16,6 @@ import (
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/adapter/outboundgroup"
"github.com/Dreamacro/clash/adapter/provider"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/utils"
"github.com/Dreamacro/clash/component/auth"
"github.com/Dreamacro/clash/component/dialer"
@ -35,7 +34,6 @@ import (
L "github.com/Dreamacro/clash/listener"
LC "github.com/Dreamacro/clash/listener/config"
"github.com/Dreamacro/clash/log"
rewrites "github.com/Dreamacro/clash/rewrite"
R "github.com/Dreamacro/clash/rules"
RP "github.com/Dreamacro/clash/rules/provider"
T "github.com/Dreamacro/clash/tunnel"
@ -61,7 +59,6 @@ type General struct {
Sniffing bool `json:"sniffing"`
EBpf EBpf `json:"-"`
GlobalClientFingerprint string `json:"global-client-fingerprint"`
KeepAliveInterval int `json:"keep-alive-interval"`
}
// Inbound config
@ -80,7 +77,6 @@ type Inbound struct {
BindAddress string `json:"bind-address"`
InboundTfo bool `json:"inbound-tfo"`
InboundMPTCP bool `json:"inbound-mptcp"`
MitmPort int `json:"mitm-port"`
}
// Controller config
@ -91,14 +87,6 @@ type Controller struct {
Secret string `json:"-"`
}
// NTP config
type NTP struct {
Enable bool `yaml:"enable"`
Server string `yaml:"server"`
Port int `yaml:"port"`
Interval int `yaml:"interval"`
}
// DNS config
type DNS struct {
Enable bool `yaml:"enable"`
@ -154,12 +142,6 @@ type Sniffer struct {
ParsePureIp bool
}
// Mitm config
type Mitm struct {
Port int `yaml:"port" json:"port"`
Rules C.RewriteRule `yaml:"rules" json:"rules"`
}
// Experimental config
type Experimental struct {
Fingerprints []string `yaml:"fingerprints"`
@ -169,8 +151,6 @@ type Experimental struct {
type Config struct {
General *General
IPTables *IPTables
Mitm *Mitm
NTP *NTP
DNS *DNS
Experimental *Experimental
Hosts *trie.DomainTrie[resolver.HostValue]
@ -187,13 +167,6 @@ type Config struct {
TLS *TLS
}
type RawNTP struct {
Enable bool `yaml:"enable"`
Server string `yaml:"server"`
ServerPort int `yaml:"server-port"`
Interval int `yaml:"interval"`
}
type RawDNS struct {
Enable bool `yaml:"enable"`
PreferH3 bool `yaml:"prefer-h3"`
@ -262,18 +235,12 @@ type RawTuicServer struct {
CWND int `yaml:"cwnd" json:"cwnd,omitempty"`
}
type RawMitm struct {
Port int `yaml:"port" json:"port"`
Rules []rewrites.RawMitmRule `yaml:"rules" json:"rules"`
}
type RawConfig struct {
Port int `yaml:"port"`
SocksPort int `yaml:"socks-port"`
RedirPort int `yaml:"redir-port"`
TProxyPort int `yaml:"tproxy-port"`
MixedPort int `yaml:"mixed-port"`
MitmPort int `yaml:"mitm-port"`
ShadowSocksConfig string `yaml:"ss-config"`
VmessConfig string `yaml:"vmess-config"`
InboundTfo bool `yaml:"inbound-tfo"`
@ -297,19 +264,16 @@ type RawConfig struct {
TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"`
GlobalClientFingerprint string `yaml:"global-client-fingerprint"`
KeepAliveInterval int `yaml:"keep-alive-interval"`
Sniffer RawSniffer `yaml:"sniffer"`
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
RuleProvider map[string]map[string]any `yaml:"rule-providers"`
Hosts map[string]any `yaml:"hosts"`
NTP RawNTP `yaml:"ntp"`
DNS RawDNS `yaml:"dns"`
Tun RawTun `yaml:"tun"`
TuicServer RawTuicServer `yaml:"tuic-server"`
EBpf EBpf `yaml:"ebpf"`
IPTables IPTables `yaml:"iptables"`
MITM RawMitm `yaml:"mitm"`
Experimental Experimental `yaml:"experimental"`
Profile Profile `yaml:"profile"`
GeoXUrl GeoXUrl `yaml:"geox-url"`
@ -454,10 +418,6 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
ParsePureIp: true,
OverrideDest: true,
},
MITM: RawMitm{
Port: 0,
Rules: []rewrites.RawMitmRule{},
},
Profile: Profile{
StoreSelected: true,
},
@ -533,9 +493,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
}
config.Hosts = hosts
ntpCfg := paresNTP(rawCfg)
config.NTP = ntpCfg
dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders)
if err != nil {
return nil, err
@ -552,12 +509,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
return nil, err
}
mitm, err := parseMitm(rawCfg.MITM)
if err != nil {
return nil, err
}
config.Mitm = mitm
config.Users = parseAuthentication(rawCfg.Authentication)
config.Tunnels = rawCfg.Tunnels
@ -588,11 +539,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
C.GeoSiteUrl = cfg.GeoXUrl.GeoSite
C.MmdbUrl = cfg.GeoXUrl.Mmdb
C.GeodataMode = cfg.GeodataMode
if cfg.KeepAliveInterval == 0 {
cfg.KeepAliveInterval = 30
}
N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second
log.Infoln("Keep Alive Interval set %+v", N.KeepAliveInterval)
// checkout externalUI exist
if externalUI != "" {
externalUI = C.Path.Resolve(externalUI)
@ -608,7 +554,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
RedirPort: cfg.RedirPort,
TProxyPort: cfg.TProxyPort,
MixedPort: cfg.MixedPort,
MitmPort: cfg.MitmPort,
ShadowSocksConfig: cfg.ShadowSocksConfig,
VmessConfig: cfg.VmessConfig,
AllowLan: cfg.AllowLan,
@ -635,7 +580,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
FindProcessMode: cfg.FindProcessMode,
EBpf: cfg.EBpf,
GlobalClientFingerprint: cfg.GlobalClientFingerprint,
KeepAliveInterval: cfg.KeepAliveInterval,
}, nil
}
@ -656,11 +600,6 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
proxies["PASS"] = adapter.NewProxy(outbound.NewPass())
proxyList = append(proxyList, "DIRECT", "REJECT")
if cfg.MITM.Port != 0 {
proxies["MITM"] = adapter.NewProxy(outbound.NewMitm(fmt.Sprintf("127.0.0.1:%d", cfg.MITM.Port)))
proxyList = append(proxyList, "MITM")
}
// parse proxy
for idx, mapping := range proxiesConfig {
proxy, err := adapter.ParseProxy(mapping)
@ -941,14 +880,6 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) {
_ = tree.Insert(domain, value)
}
}
if cfg.MITM.Port != 0 {
value, _ := resolver.NewHostValue("8.8.9.9")
if err := tree.Insert("mitm.clash", value); err != nil {
log.Errorln("insert mitm.clash to host error: %s", err.Error())
}
}
tree.Optimize()
return tree, nil
@ -1201,29 +1132,6 @@ func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]*router.DomainM
return sites, nil
}
func paresNTP(rawCfg *RawConfig) *NTP {
var server = "time.apple.com"
var port = 123
var interval = 30
cfg := rawCfg.NTP
if len(cfg.Server) != 0 {
server = cfg.Server
}
if cfg.ServerPort != 0 {
port = cfg.ServerPort
}
if cfg.Interval != 0 {
interval = cfg.Interval
}
ntpCfg := &NTP{
Enable: cfg.Enable,
Server: server,
Port: port,
Interval: interval,
}
return ntpCfg
}
func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) {
cfg := rawCfg.DNS
if cfg.Enable && len(cfg.NameServer) == 0 {
@ -1497,28 +1405,3 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
return sniffer, nil
}
func parseMitm(rawMitm RawMitm) (*Mitm, error) {
var (
req []C.Rewrite
res []C.Rewrite
)
for _, line := range rawMitm.Rules {
rule, err := rewrites.ParseRewrite(line)
if err != nil {
return nil, fmt.Errorf("parse rewrite rule failure: %w", err)
}
if rule.RuleType() == C.MitmResponseHeader || rule.RuleType() == C.MitmResponseBody {
res = append(res, rule)
} else {
req = append(req, rule)
}
}
return &Mitm{
Port: rawMitm.Port,
Rules: rewrites.NewRewriteRules(req, res),
}, nil
}

View File

@ -19,7 +19,6 @@ const (
Direct AdapterType = iota
Reject
Compatible
Mitm
Pass
Relay
@ -183,8 +182,6 @@ func (at AdapterType) String() string {
return "Compatible"
case Pass:
return "Pass"
case Mitm:
return "Mitm"
case Shadowsocks:
return "Shadowsocks"
case ShadowsocksR:
@ -270,17 +267,13 @@ type NatTable interface {
Delete(key string)
DeleteLock(key string)
GetLocalConn(lAddr, rAddr string) *net.UDPConn
GetForLocalConn(lAddr, rAddr string) *net.UDPConn
AddLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool
AddForLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool
RangeLocalConn(lAddr string, f func(key, value any) bool)
RangeForLocalConn(lAddr string, f func(key string, value *net.UDPConn) bool)
GetOrCreateLockForLocalConn(lAddr, key string) (*sync.Cond, bool)
GetOrCreateLockForLocalConn(lAddr string, key string) (*sync.Cond, bool)
DeleteForLocalConn(lAddr, key string)
DeleteLockForLocalConn(lAddr, key string)
DeleteLocalConnMap(lAddr, key string)
}

View File

@ -31,7 +31,6 @@ const (
TUN
TUIC
INNER
MITM
)
type NetWork int
@ -81,8 +80,6 @@ func (t Type) String() string {
return "Tuic"
case INNER:
return "Inner"
case MITM:
return "Mitm"
default:
return "Unknown"
}
@ -147,8 +144,6 @@ type Metadata struct {
RemoteDst string `json:"remoteDestination"`
// Only domain rule
SniffHost string `json:"sniffHost"`
// Only Mitm rule
UserAgent string `json:"userAgent"`
}
func (m *Metadata) RemoteAddress() string {
@ -162,8 +157,6 @@ func (m *Metadata) SourceAddress() string {
func (m *Metadata) SourceDetail() string {
if m.Type == INNER {
return fmt.Sprintf("%s", ClashName)
} else if m.Type == MITM {
return fmt.Sprintf("%s-MITM", ClashName)
}
switch {

View File

@ -148,12 +148,8 @@ func (p *path) GeoSite() string {
return P.Join(p.homeDir, "GeoSite.dat")
}
func (p *path) RootCA() string {
return p.Resolve("mitm_ca.crt")
}
func (p *path) CAKey() string {
return p.Resolve("mitm_ca.key")
func (p *path) GetAssetLocation(file string) string {
return P.Join(p.homeDir, file)
}
func (p *path) GetExecutableFullPath() string {

View File

@ -1,120 +0,0 @@
package constant
import (
"encoding/json"
"errors"
regexp "github.com/dlclark/regexp2"
)
var RewriteTypeMapping = map[string]RewriteType{
MitmReject.String(): MitmReject,
MitmReject200.String(): MitmReject200,
MitmRejectImg.String(): MitmRejectImg,
MitmRejectDict.String(): MitmRejectDict,
MitmRejectArray.String(): MitmRejectArray,
Mitm302.String(): Mitm302,
Mitm307.String(): Mitm307,
MitmRequestHeader.String(): MitmRequestHeader,
MitmRequestBody.String(): MitmRequestBody,
MitmResponseHeader.String(): MitmResponseHeader,
MitmResponseBody.String(): MitmResponseBody,
}
const (
MitmReject RewriteType = iota
MitmReject200
MitmRejectImg
MitmRejectDict
MitmRejectArray
Mitm302
Mitm307
MitmRequestHeader
MitmRequestBody
MitmResponseHeader
MitmResponseBody
)
type RewriteType int
// UnmarshalYAML unserialize RewriteType with yaml
func (e *RewriteType) UnmarshalYAML(unmarshal func(any) error) error {
var tp string
if err := unmarshal(&tp); err != nil {
return err
}
mode, exist := RewriteTypeMapping[tp]
if !exist {
return errors.New("invalid MITM Action")
}
*e = mode
return nil
}
// MarshalYAML serialize RewriteType with yaml
func (e RewriteType) MarshalYAML() (any, error) {
return e.String(), nil
}
// UnmarshalJSON unserialize RewriteType with json
func (e *RewriteType) UnmarshalJSON(data []byte) error {
var tp string
json.Unmarshal(data, &tp)
mode, exist := RewriteTypeMapping[tp]
if !exist {
return errors.New("invalid MITM Action")
}
*e = mode
return nil
}
// MarshalJSON serialize RewriteType with json
func (e RewriteType) MarshalJSON() ([]byte, error) {
return json.Marshal(e.String())
}
func (rt RewriteType) String() string {
switch rt {
case MitmReject:
return "reject" // 404
case MitmReject200:
return "reject-200"
case MitmRejectImg:
return "reject-img"
case MitmRejectDict:
return "reject-dict"
case MitmRejectArray:
return "reject-array"
case Mitm302:
return "302"
case Mitm307:
return "307"
case MitmRequestHeader:
return "request-header"
case MitmRequestBody:
return "request-body"
case MitmResponseHeader:
return "response-header"
case MitmResponseBody:
return "response-body"
default:
return "Unknown"
}
}
type Rewrite interface {
ID() string
URLRegx() *regexp.Regexp
RuleType() RewriteType
RuleRegx() *regexp.Regexp
RulePayload() string
ReplaceURLPayload([]string) string
ReplaceSubPayload(string) string
}
type RewriteRule interface {
SearchInRequest(func(Rewrite) bool) bool
SearchInResponse(func(Rewrite) bool) bool
}

View File

@ -23,7 +23,6 @@ const (
Network
Uid
SubRules
UserAgent
MATCH
AND
OR
@ -68,8 +67,6 @@ func (rt RuleType) String() string {
return "Process"
case ProcessPath:
return "ProcessPath"
case UserAgent:
return "UserAgent"
case MATCH:
return "Match"
case RuleSet:

View File

@ -50,8 +50,6 @@ external-ui: /path/to/ui/folder # 配置 WEB UI 目录,使用 http://{{externa
# Utls is currently support TLS transport in TCP/grpc/WS/HTTP for VLESS/Vmess and trojan.
global-client-fingerprint: chrome
keep-alive-interval: 30
# routing-mark:6666 # 配置 fwmark 仅用于 Linux
experimental:
@ -683,11 +681,6 @@ proxies: # socks5
# skip-cert-verify: true
# max-open-streams: 20 # default 100, too many open streams may hurt performance
# sni: example.com
#
# meta和sing-box私有扩展将ss-uot用于udp中继开启此选项后udp-relay-mode将失效
# 警告与原版tuic不兼容
# udp-over-stream: false
# udp-over-stream-version: 1
# ShadowsocksR
# The supported ciphers (encryption methods): all stream ciphers in ss

29
go.mod
View File

@ -3,16 +3,14 @@ module github.com/Dreamacro/clash
go 1.20
require (
github.com/3andne/restls-client-go v0.1.6
github.com/3andne/restls-client-go v0.1.4
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
github.com/beevik/ntp v1.3.0
github.com/cilium/ebpf v0.11.0
github.com/coreos/go-iptables v0.7.0
github.com/dlclark/regexp2 v1.10.0
github.com/go-chi/chi/v5 v5.0.10
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.3
github.com/gofrs/uuid v4.4.0+incompatible
github.com/gofrs/uuid/v5 v5.0.0
github.com/gorilla/websocket v1.5.0
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c
@ -21,38 +19,35 @@ require (
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/mdlayher/netlink v1.7.2
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28
github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86
github.com/metacubex/sing-shadowsocks v0.2.4
github.com/metacubex/sing-shadowsocks2 v0.1.3
github.com/metacubex/sing-tun v0.1.11
github.com/metacubex/sing-vmess v0.1.8-0.20230801054944-603005461ff8
github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28
github.com/miekg/dns v1.1.55
github.com/mroth/weightedrand/v2 v2.1.0
github.com/mroth/weightedrand/v2 v2.0.2
github.com/openacid/low v0.1.21
github.com/oschwald/maxminddb-golang v1.12.0
github.com/puzpuzpuz/xsync/v2 v2.5.0
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c
github.com/sagernet/sing v0.2.9
github.com/sagernet/sing-mux v0.1.2
github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
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-20230807125731-5d4a7ef2dc5f
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77
github.com/samber/lo v1.38.1
github.com/shirou/gopsutil/v3 v3.23.7
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
github.com/zhangyunhao116/fastrand v0.3.0
go.etcd.io/bbolt v1.3.7
go.uber.org/atomic v1.9.0
go.uber.org/automaxprocs v1.5.3
golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b
golang.org/x/net v0.14.0
golang.org/x/sync v0.3.0
golang.org/x/sys v0.11.0
golang.org/x/text v0.12.0
google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.2.1
@ -69,7 +64,6 @@ require (
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/gaukas/godicttls v0.0.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect
@ -78,7 +72,7 @@ require (
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.16.7 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 // indirect
@ -88,7 +82,7 @@ require (
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-20 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.3.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/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
@ -103,8 +97,9 @@ require (
github.com/yusufpapurcu/wmi v1.2.3 // indirect
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.9.1 // indirect
)
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230714010500-e24664dc75a7

78
go.sum
View File

@ -1,5 +1,5 @@
github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08=
github.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY=
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=
@ -10,8 +10,6 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/beevik/ntp v1.3.0 h1:/w5VhpW5BGKS37vFm1p9oVk/t4HnnkKZAZIubHM6F7Q=
github.com/beevik/ntp v1.3.0/go.mod h1:vD6h1um4kzXpqmLTuu0cCLcC+NfvC0IC+ltmEDA8E78=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@ -37,8 +35,6 @@ github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtB
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
@ -50,8 +46,6 @@ 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-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
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/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/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
@ -80,8 +74,8 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
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/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
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.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -98,10 +92,10 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 h1:qSEOvPPaMrWggFyFhFYGyMR8i1HKyhXjdi1QYUAa2ww=
github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475/go.mod h1:wehEpqiogdeyncfhckJP5gD2LtBgJW0wnDC24mJ+8Jg=
github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 h1:ggSo4B1LDH9ZIROoUibxlrUpi7YCMri7HMXn4aNQkiM=
github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28/go.mod h1:SthFvvoqgrEUgIxQXRnqdUAAYQECBavkhl7iA0geVd8=
github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579 h1:dE1dBB6CTzNdSMFTE5OCHvzHLewiqiA1nhD+7egtvAc=
github.com/metacubex/sing v0.0.0-20230817143035-28d23f152579/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA=
github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86 h1:qGExcB3lYk51LPEJh5HTdQplbZmuTn+tkcuhuas1LC8=
github.com/metacubex/quic-go v0.37.4-0.20230809092428-5acf8eb2de86/go.mod h1:HhHoyskMk4kzfLPKcm7EF7pGXF89KRVwjbGrEaN6lIU=
github.com/metacubex/sing v0.0.0-20230714010500-e24664dc75a7 h1:XY3Y6nPL45XuN/k3rDXJ1TJknLo8rTo1SVuDOmOEf4E=
github.com/metacubex/sing v0.0.0-20230714010500-e24664dc75a7/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/metacubex/sing-shadowsocks v0.2.4 h1:Gc99Z17JVif1PKKq1pjqhSmc2kvHUgk+AqxOstCzhQ0=
github.com/metacubex/sing-shadowsocks v0.2.4/go.mod h1:w9qoEZSh9aKeXSLXHe0DGbG2UE9/2VlLGwukzQZ7byI=
github.com/metacubex/sing-shadowsocks2 v0.1.3 h1:nZvH+4jQXZ92NeNdR9fXaUGTPNJPt6u0nkcuh/NEt5Y=
@ -114,8 +108,8 @@ github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28 h1:mXFpxf
github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28/go.mod h1:KrDPq/dE793jGIJw9kcIvjA/proAfU0IeU7WlMXW7rs=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
github.com/mroth/weightedrand/v2 v2.0.2 h1:A8wJRUBcfguGl6oOQHI8fy5P4ViGRT9hdQdlG/7RiXo=
github.com/mroth/weightedrand/v2 v2.0.2/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
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.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
@ -136,29 +130,27 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
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/puzpuzpuz/xsync/v2 v2.5.0 h1:2k4qrO/orvmEXZ3hmtHqIy9XaQtPTwzMZk1+iErpE8c=
github.com/puzpuzpuz/xsync/v2 v2.5.0/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI=
github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/qtls-go1-20 v0.3.1 h1:O4BLOM3hwfVF3AcktIylQXyl7Yi2iBNVy5QsV+ySxbg=
github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
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-mux v0.1.3-0.20230811111955-dc1639b5204c h1:35/FowAvt3Z62mck0TXzVc4jS5R5CWq62qcV2P1cp0I=
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY=
github.com/sagernet/sing-mux v0.1.2 h1:av2/m6e+Gh+ECTuJZqYCjJz55BNkot0VyRMkREqyF/g=
github.com/sagernet/sing-mux v0.1.2/go.mod h1:r2V8AlOzXaRCHXK7fILCUGzuI2iILweTaG8C5xlpHxo=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q=
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M=
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-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho=
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk=
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/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
@ -199,7 +191,6 @@ github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1
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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig=
@ -208,39 +199,26 @@ gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiV
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI=
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -254,30 +232,18 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@ -286,8 +252,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
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=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -2,15 +2,11 @@ package executor
import (
"fmt"
"github.com/Dreamacro/clash/ntp"
"net"
"net/netip"
"os"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/adapter/inbound"
@ -91,13 +87,11 @@ func ApplyConfig(cfg *config.Config, force bool) {
}
updateUsers(cfg.Users)
updateProxies(cfg.Mitm, cfg.Proxies, cfg.Providers)
updateProxies(cfg.Proxies, cfg.Providers)
updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders)
updateSniffer(cfg.Sniffer)
updateHosts(cfg.Hosts)
updateMitm(cfg.Mitm)
updateGeneral(cfg.General)
updateNTP(cfg.NTP)
updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6)
updateListeners(cfg.General, cfg.Listeners, force)
updateIPTables(cfg)
@ -135,7 +129,6 @@ func GetGeneral() *config.General {
RedirPort: ports.RedirPort,
TProxyPort: ports.TProxyPort,
MixedPort: ports.MixedPort,
MitmPort: ports.MitmPort,
Tun: listener.GetTunConf(),
TuicServer: listener.GetTuicConf(),
ShadowSocksConfig: ports.ShadowSocksConfig,
@ -185,13 +178,6 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList
func updateExperimental(c *config.Config) {
}
func updateNTP(c *config.NTP) {
if c.Enable {
ntp.ReCreateNTPService(net.JoinHostPort(c.Server, strconv.Itoa(c.Port)),
time.Duration(c.Interval))
}
}
func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, generalIPv6 bool) {
if !c.Enable {
resolver.DefaultResolver = nil
@ -264,7 +250,7 @@ func updateHosts(tree *trie.DomainTrie[resolver.HostValue]) {
resolver.DefaultHosts = resolver.NewHosts(tree)
}
func updateProxies(mitm *config.Mitm, proxies map[string]C.Proxy, providers map[string]provider.ProxyProvider) {
func updateProxies(proxies map[string]C.Proxy, providers map[string]provider.ProxyProvider) {
tunnel.UpdateProxies(proxies, providers)
}
@ -492,13 +478,8 @@ func updateIPTables(cfg *config.Config) {
log.Infoln("[IPTABLES] Setting iptables completed")
}
func updateMitm(mitm *config.Mitm) {
listener.ReCreateMitm(mitm.Port, tunnel.TCPIn())
tunnel.UpdateRewrites(mitm.Rules)
}
func Shutdown() {
listener.Cleanup()
listener.Cleanup(false)
tproxy.CleanupTProxyIPTables()
resolver.StoreFakePoolState()

View File

@ -40,7 +40,6 @@ type configSchema struct {
RedirPort *int `json:"redir-port"`
TProxyPort *int `json:"tproxy-port"`
MixedPort *int `json:"mixed-port"`
MitmPort *int `json:"mitm-port"`
Tun *tunSchema `json:"tun"`
TuicServer *tuicServerSchema `json:"tuic-server"`
ShadowSocksConfig *string `json:"ss-config"`
@ -263,7 +262,6 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
P.ReCreateShadowSocks(pointerOrDefaultString(general.ShadowSocksConfig, ports.ShadowSocksConfig), tcpIn, udpIn)
P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tcpIn, udpIn)
P.ReCreateTuic(pointerOrDefaultTuicServer(general.TuicServer, P.LastTuicConf), tcpIn, udpIn)
P.ReCreateMitm(pointerOrDefault(general.MitmPort, ports.MitmPort), tcpIn)
if general.Mode != nil {
tunnel.SetMode(*general.Mode)

View File

@ -9,7 +9,6 @@ import (
"time"
"github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/adapter/outboundgroup"
"github.com/Dreamacro/clash/common/utils"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/tunnel"
@ -58,11 +57,6 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) {
return
}
if proxy.(*adapter.Proxy).Type() == C.URLTest {
URLTestGroup := proxy.(*adapter.Proxy).ProxyAdapter.(*outboundgroup.URLTest)
URLTestGroup.ForceSet("")
}
query := r.URL.Query()
url := query.Get("url")
timeout, err := strconv.ParseInt(query.Get("timeout"), 10, 32)
@ -83,6 +77,7 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) {
defer cancel()
dm, err := group.URLTest(ctx, url, expectedStatus)
if err != nil {
render.Status(r, http.StatusGatewayTimeout)
render.JSON(w, r, newError(err.Error()))

View File

@ -8,7 +8,7 @@ import (
"runtime"
"syscall"
"github.com/Dreamacro/clash/hub/executor"
"github.com/Dreamacro/clash/listener"
"github.com/Dreamacro/clash/log"
"github.com/go-chi/chi/v5"
@ -39,12 +39,12 @@ 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 restartExecutable(execPath)
go runRestart(execPath)
}
func restartExecutable(execPath string) {
func runRestart(execPath string) {
var err error
executor.Shutdown()
listener.Cleanup(false)
if runtime.GOOS == "windows" {
cmd := exec.Command(execPath, os.Args[1:]...)
log.Infoln("restarting: %q %q", execPath, os.Args[1:])

View File

@ -41,5 +41,5 @@ func upgrade(w http.ResponseWriter, r *http.Request) {
f.Flush()
}
go restartExecutable(execPath)
go runRestart(execPath)
}

View File

@ -5,7 +5,6 @@ import (
"net/netip"
"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"
@ -56,7 +55,7 @@ func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) {
return
}
N.TCPKeepAlive(conn)
_ = conn.(*net.TCPConn).SetKeepAlive(true)
in <- inbound.NewSocket(target, conn, C.REDIR, l.additions...)
}

View File

@ -36,7 +36,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[strin
var resp *http.Response
if !trusted {
resp = Authenticate(request, cache)
resp = authenticate(request, cache)
trusted = resp == nil
}
@ -66,19 +66,19 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[strin
return // hijack connection
}
RemoveHopByHopHeaders(request.Header)
RemoveExtraHTTPHostPort(request)
removeHopByHopHeaders(request.Header)
removeExtraHTTPHostPort(request)
if request.URL.Scheme == "" || request.URL.Host == "" {
resp = ResponseWith(request, http.StatusBadRequest)
resp = responseWith(request, http.StatusBadRequest)
} else {
resp, err = client.Do(request)
if err != nil {
resp = ResponseWith(request, http.StatusBadGateway)
resp = responseWith(request, http.StatusBadGateway)
}
}
RemoveHopByHopHeaders(resp.Header)
removeHopByHopHeaders(resp.Header)
}
if keepAlive {
@ -98,12 +98,12 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[strin
_ = conn.Close()
}
func Authenticate(request *http.Request, cache *cache.LruCache[string, bool]) *http.Response {
func authenticate(request *http.Request, cache *cache.LruCache[string, bool]) *http.Response {
authenticator := authStore.Authenticator()
if authenticator != nil {
credential := parseBasicProxyAuthorization(request)
if credential == "" {
resp := ResponseWith(request, http.StatusProxyAuthRequired)
resp := responseWith(request, http.StatusProxyAuthRequired)
resp.Header.Set("Proxy-Authenticate", "Basic")
return resp
}
@ -117,14 +117,14 @@ func Authenticate(request *http.Request, cache *cache.LruCache[string, bool]) *h
if !authed {
log.Infoln("Auth failed from %s", request.RemoteAddr)
return ResponseWith(request, http.StatusForbidden)
return responseWith(request, http.StatusForbidden)
}
}
return nil
}
func ResponseWith(request *http.Request, statusCode int) *http.Response {
func responseWith(request *http.Request, statusCode int) *http.Response {
return &http.Response{
StatusCode: statusCode,
Status: http.StatusText(statusCode),

View File

@ -6,7 +6,6 @@ import (
"net"
"net/http"
"strings"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
N "github.com/Dreamacro/clash/common/net"
@ -30,7 +29,7 @@ func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext
defer conn.Close()
removeProxyHeaders(request.Header)
RemoveExtraHTTPHostPort(request)
removeExtraHTTPHostPort(request)
address := request.Host
if _, _, err := net.SplitHostPort(address); err != nil {
@ -88,65 +87,3 @@ func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext
N.Relay(bufferedLeft, conn)
}
}
func HandleUpgradeY(localConn net.Conn, serverConn *N.BufferedConn, request *http.Request, in chan<- C.ConnContext) (resp *http.Response) {
removeProxyHeaders(request.Header)
RemoveExtraHTTPHostPort(request)
if serverConn == nil {
address := request.Host
if _, _, err := net.SplitHostPort(address); err != nil {
port := "80"
if request.TLS != nil {
port = "443"
}
address = net.JoinHostPort(address, port)
}
dstAddr := socks5.ParseAddr(address)
if dstAddr == nil {
return
}
left, right := net.Pipe()
in <- inbound.NewHTTP(dstAddr, localConn.RemoteAddr(), right)
serverConn = N.NewBufferedConn(left)
defer func() {
_ = serverConn.Close()
}()
}
err := request.Write(serverConn)
if err != nil {
_ = localConn.Close()
return
}
resp, err = http.ReadResponse(serverConn.Reader(), request)
if err != nil {
_ = localConn.Close()
return
}
if resp.StatusCode == http.StatusSwitchingProtocols {
removeProxyHeaders(resp.Header)
err = localConn.SetReadDeadline(time.Time{}) // set to not time out
if err != nil {
return
}
err = resp.Write(localConn)
if err != nil {
return
}
N.Relay(serverConn, localConn) // blocking here
_ = localConn.Close()
resp = nil
}
return
}

View File

@ -15,8 +15,8 @@ func removeProxyHeaders(header http.Header) {
header.Del("Proxy-Authorization")
}
// RemoveHopByHopHeaders remove hop-by-hop header
func RemoveHopByHopHeaders(header http.Header) {
// removeHopByHopHeaders remove hop-by-hop header
func removeHopByHopHeaders(header http.Header) {
// Strip hop-by-hop header based on RFC:
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
// https://www.mnot.net/blog/2011/07/11/what_proxies_must_do
@ -38,9 +38,9 @@ func RemoveHopByHopHeaders(header http.Header) {
}
}
// RemoveExtraHTTPHostPort remove extra host port (example.com:80 --> example.com)
// removeExtraHTTPHostPort remove extra host port (example.com:80 --> example.com)
// It resolves the behavior of some HTTP servers that do not handle host:80 (e.g. baidu.com)
func RemoveExtraHTTPHostPort(req *http.Request) {
func removeExtraHTTPHostPort(req *http.Request) {
host := req.Host
if host == "" {
host = req.URL.Host

View File

@ -1,26 +1,19 @@
package listener
import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"fmt"
"golang.org/x/exp/slices"
"net"
"os"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/Dreamacro/clash/common/cert"
"github.com/Dreamacro/clash/component/ebpf"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/autoredir"
LC "github.com/Dreamacro/clash/listener/config"
"github.com/Dreamacro/clash/listener/http"
"github.com/Dreamacro/clash/listener/mitm"
"github.com/Dreamacro/clash/listener/mixed"
"github.com/Dreamacro/clash/listener/redir"
embedSS "github.com/Dreamacro/clash/listener/shadowsocks"
@ -30,10 +23,9 @@ import (
"github.com/Dreamacro/clash/listener/socks"
"github.com/Dreamacro/clash/listener/tproxy"
"github.com/Dreamacro/clash/listener/tuic"
LT "github.com/Dreamacro/clash/listener/tunnel"
"github.com/Dreamacro/clash/listener/tunnel"
"github.com/Dreamacro/clash/log"
rewrites "github.com/Dreamacro/clash/rewrite"
"github.com/samber/lo"
)
@ -50,8 +42,8 @@ var (
tproxyUDPListener *tproxy.UDPListener
mixedListener *mixed.Listener
mixedUDPLister *socks.UDPListener
tunnelTCPListeners = map[string]*LT.Listener{}
tunnelUDPListeners = map[string]*LT.PacketConn{}
tunnelTCPListeners = map[string]*tunnel.Listener{}
tunnelUDPListeners = map[string]*tunnel.PacketConn{}
inboundListeners = map[string]C.InboundListener{}
tunLister *sing_tun.Listener
shadowSocksListener C.MultiAddrListener
@ -60,7 +52,6 @@ var (
autoRedirListener *autoredir.Listener
autoRedirProgram *ebpf.TcEBpfProgram
tcProgram *ebpf.TcEBpfProgram
mitmListener *mitm.Listener
// lock for recreate function
socksMux sync.Mutex
@ -76,7 +67,6 @@ var (
tuicMux sync.Mutex
autoRedirMux sync.Mutex
tcMux sync.Mutex
mitmMux sync.Mutex
LastTunConf LC.Tun
LastTuicConf LC.TuicServer
@ -90,12 +80,13 @@ type Ports struct {
MixedPort int `json:"mixed-port"`
ShadowSocksConfig string `json:"ss-config"`
VmessConfig string `json:"vmess-config"`
MitmPort int `json:"mitm-port"`
}
func GetTunConf() LC.Tun {
if tunLister == nil {
return LastTunConf
return LC.Tun{
Enable: false,
}
}
return tunLister.Config()
}
@ -525,7 +516,7 @@ func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.Pack
defer func() {
if err != nil {
log.Errorln("Start TUN listening error: %s", err.Error())
tunConf.Enable = false
Cleanup(false)
}
}()
@ -536,7 +527,7 @@ func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.Pack
return
}
closeTunListener()
Cleanup(true)
if !tunConf.Enable {
return
@ -710,7 +701,7 @@ func PatchTunnel(tunnels []LC.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- C
for _, elm := range needCreate {
key := fmt.Sprintf("%s/%s/%s", elm.addr, elm.target, elm.proxy)
if elm.network == "tcp" {
l, err := LT.New(elm.addr, elm.target, elm.proxy, tcpIn)
l, err := tunnel.New(elm.addr, elm.target, elm.proxy, tcpIn)
if err != nil {
log.Errorln("Start tunnel %s error: %s", elm.target, err.Error())
continue
@ -718,7 +709,7 @@ func PatchTunnel(tunnels []LC.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- C
tunnelTCPListeners[key] = l
log.Infoln("Tunnel(tcp/%s) proxy %s listening at: %s", elm.target, elm.proxy, tunnelTCPListeners[key].Address())
} else {
l, err := LT.NewUDP(elm.addr, elm.target, elm.proxy, udpIn)
l, err := tunnel.NewUDP(elm.addr, elm.target, elm.proxy, udpIn)
if err != nil {
log.Errorln("Start tunnel %s error: %s", elm.target, err.Error())
continue
@ -758,79 +749,6 @@ func PatchInboundListeners(newListenerMap map[string]C.InboundListener, tcpIn ch
}
}
func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
mitmMux.Lock()
defer mitmMux.Unlock()
var err error
defer func() {
if err != nil {
log.Errorln("Start MITM server error: %s", err.Error())
}
}()
addr := genAddr(bindAddress, port, allowLan)
if mitmListener != nil {
if mitmListener.RawAddress() == addr {
return
}
_ = mitmListener.Close()
mitmListener = nil
}
if portIsZero(addr) {
return
}
if err = initCert(); err != nil {
return
}
var (
rootCACert tls.Certificate
x509c *x509.Certificate
certOption *cert.Config
)
rootCACert, err = tls.LoadX509KeyPair(C.Path.RootCA(), C.Path.CAKey())
if err != nil {
return
}
privateKey := rootCACert.PrivateKey.(*rsa.PrivateKey)
x509c, err = x509.ParseCertificate(rootCACert.Certificate[0])
if err != nil {
return
}
certOption, err = cert.NewConfig(
x509c,
privateKey,
)
if err != nil {
return
}
certOption.SetValidity(time.Hour * 24 * 365 * 2) // 2 years
certOption.SetOrganization("Clash ManInTheMiddle Proxy Services")
opt := &mitm.Option{
Addr: addr,
ApiHost: "mitm.clash",
CertConfig: certOption,
Handler: &rewrites.RewriteHandler{},
}
mitmListener, err = mitm.New(opt, tcpIn)
if err != nil {
return
}
log.Infoln("Mitm proxy listening at: %s", mitmListener.Address())
}
// GetPorts return the ports of proxy servers
func GetPorts() *Ports {
ports := &Ports{}
@ -873,12 +791,6 @@ func GetPorts() *Ports {
ports.VmessConfig = vmessListener.Config()
}
if mitmListener != nil {
_, portStr, _ := net.SplitHostPort(mitmListener.Address())
port, _ := strconv.Atoi(portStr)
ports.MitmPort = port
}
return ports
}
@ -985,26 +897,10 @@ func hasTunConfigChange(tunConf *LC.Tun) bool {
return false
}
func closeTunListener() {
func Cleanup(wait bool) {
if tunLister != nil {
tunLister.Close()
tunLister = nil
}
}
func initCert() error {
if _, err := os.Stat(C.Path.RootCA()); os.IsNotExist(err) {
log.Infoln("Can't find mitm_ca.crt, start generate")
err = cert.GenerateAndSave(C.Path.RootCA(), C.Path.CAKey())
if err != nil {
return err
}
log.Infoln("Generated CA private key and CA certificate finish")
}
return nil
}
func Cleanup() {
closeTunListener()
LastTunConf = LC.Tun{}
}

View File

@ -1,55 +0,0 @@
package mitm
import (
"context"
"crypto/tls"
"net"
"net/http"
"github.com/Dreamacro/clash/adapter/inbound"
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5"
)
func getServerConn(serverConn *N.BufferedConn, request *http.Request, srcAddr net.Addr, in chan<- C.ConnContext) (*N.BufferedConn, error) {
if serverConn != nil {
return serverConn, nil
}
address := request.URL.Host
if _, _, err := net.SplitHostPort(address); err != nil {
port := "80"
if request.TLS != nil {
port = "443"
}
address = net.JoinHostPort(address, port)
}
dstAddr := socks5.ParseAddr(address)
if dstAddr == nil {
return nil, socks5.ErrAddressNotSupported
}
left, right := net.Pipe()
in <- inbound.NewMitm(dstAddr, srcAddr, request.Header.Get("User-Agent"), right)
if request.TLS != nil {
tlsConn := tls.Client(left, &tls.Config{
ServerName: request.TLS.ServerName,
})
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
defer cancel()
if err := tlsConn.HandshakeContext(ctx); err != nil {
return nil, err
}
serverConn = N.NewBufferedConn(tlsConn)
} else {
serverConn = N.NewBufferedConn(left)
}
return serverConn, nil
}

View File

@ -1,9 +0,0 @@
package mitm
import (
_ "net/http"
_ "unsafe"
)
//go:linkname validMethod net/http.validMethod
func validMethod(method string) bool

View File

@ -1,349 +0,0 @@
package mitm
import (
"bufio"
"bytes"
"context"
"crypto/tls"
"encoding/pem"
"fmt"
"io"
"net"
"net/http"
"net/netip"
"os"
"strings"
"time"
"github.com/Dreamacro/clash/common/cache"
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
H "github.com/Dreamacro/clash/listener/http"
)
func HandleConn(c net.Conn, opt *Option, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) {
var (
clientIP = netip.MustParseAddrPort(c.RemoteAddr().String()).Addr()
sourceAddr net.Addr
serverConn *N.BufferedConn
connState *tls.ConnectionState
)
defer func() {
if serverConn != nil {
_ = serverConn.Close()
}
}()
conn := N.NewBufferedConn(c)
trusted := cache == nil // disable authenticate if cache is nil
if !trusted {
trusted = clientIP.IsLoopback() || clientIP.IsUnspecified()
}
readLoop:
for {
// use SetReadDeadline instead of Proxy-Connection keep-alive
if err := conn.SetReadDeadline(time.Now().Add(65 * time.Second)); err != nil {
break
}
request, err := H.ReadRequest(conn.Reader())
if err != nil {
break
}
var response *http.Response
session := newSession(conn, request, response)
sourceAddr = parseSourceAddress(session.request, conn.RemoteAddr(), sourceAddr)
session.request.RemoteAddr = sourceAddr.String()
if !trusted {
session.response = H.Authenticate(session.request, cache)
trusted = session.response == nil
}
if trusted {
if session.request.Method == http.MethodConnect {
if session.request.ProtoMajor > 1 {
session.request.ProtoMajor = 1
session.request.ProtoMinor = 1
}
// Manual writing to support CONNECT for http 1.0 (workaround for uplay client)
if _, err = fmt.Fprintf(session.conn, "HTTP/%d.%d %03d %s\r\n\r\n", session.request.ProtoMajor, session.request.ProtoMinor, http.StatusOK, "Connection established"); err != nil {
handleError(opt, session, err)
break // close connection
}
if strings.HasSuffix(session.request.URL.Host, ":80") {
goto readLoop
}
b, err1 := conn.Peek(1)
if err1 != nil {
handleError(opt, session, err1)
break // close connection
}
// TLS handshake.
if b[0] == 0x16 {
tlsConn := tls.Server(conn, opt.CertConfig.NewTLSConfigForHost(session.request.URL.Hostname()))
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
// handshake with the local client
if err = tlsConn.HandshakeContext(ctx); err != nil {
cancel()
session.response = session.NewErrorResponse(fmt.Errorf("handshake failed: %w", err))
_ = writeResponse(session, false)
break // close connection
}
cancel()
cs := tlsConn.ConnectionState()
connState = &cs
conn = N.NewBufferedConn(tlsConn)
}
if strings.HasSuffix(session.request.URL.Host, ":443") {
goto readLoop
}
if conn.SetReadDeadline(time.Now().Add(time.Second)) != nil {
break
}
buf, err2 := conn.Peek(7)
if err2 != nil {
if err2 != bufio.ErrBufferFull && !os.IsTimeout(err2) {
handleError(opt, session, err2)
break // close connection
}
}
// others protocol over tcp
if !isHTTPTraffic(buf) {
if connState != nil {
session.request.TLS = connState
}
serverConn, err = getServerConn(serverConn, session.request, sourceAddr, in)
if err != nil {
break
}
if conn.SetReadDeadline(time.Time{}) != nil {
break
}
N.Relay(serverConn, conn)
return // hijack connection
}
goto readLoop
}
prepareRequest(connState, session.request)
// hijack api
if session.request.URL.Hostname() == opt.ApiHost {
if err = handleApiRequest(session, opt); err != nil {
handleError(opt, session, err)
}
break
}
// forward websocket
if isWebsocketRequest(request) {
serverConn, err = getServerConn(serverConn, session.request, sourceAddr, in)
if err != nil {
break
}
session.request.RequestURI = ""
if session.response = H.HandleUpgradeY(conn, serverConn, request, in); session.response == nil {
return // hijack connection
}
}
if session.response == nil {
H.RemoveHopByHopHeaders(session.request.Header)
H.RemoveExtraHTTPHostPort(session.request)
// hijack custom request and write back custom response if necessary
newReq, newRes := opt.Handler.HandleRequest(session)
if newReq != nil {
session.request = newReq
}
if newRes != nil {
session.response = newRes
if err = writeResponse(session, false); err != nil {
handleError(opt, session, err)
break
}
continue
}
session.request.RequestURI = ""
if session.request.URL.Host == "" {
session.response = session.NewErrorResponse(ErrInvalidURL)
} else {
serverConn, err = getServerConn(serverConn, session.request, sourceAddr, in)
if err != nil {
break
}
// send the request to remote server
err = session.request.Write(serverConn)
if err != nil {
break
}
session.response, err = http.ReadResponse(serverConn.Reader(), request)
if err != nil {
break
}
}
}
}
if err = writeResponseWithHandler(session, opt); err != nil {
handleError(opt, session, err)
break // close connection
}
}
_ = conn.Close()
}
func writeResponseWithHandler(session *Session, opt *Option) error {
res := opt.Handler.HandleResponse(session)
if res != nil {
session.response = res
}
return writeResponse(session, true)
}
func writeResponse(session *Session, keepAlive bool) error {
H.RemoveHopByHopHeaders(session.response.Header)
if keepAlive {
session.response.Header.Set("Connection", "keep-alive")
session.response.Header.Set("Keep-Alive", "timeout=60")
}
return session.writeResponse()
}
func handleApiRequest(session *Session, opt *Option) error {
if opt.CertConfig != nil && strings.ToLower(session.request.URL.Path) == "/cert.crt" {
b := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: opt.CertConfig.GetCA().Raw,
})
session.response = session.NewResponse(http.StatusOK, bytes.NewReader(b))
session.response.Close = true
session.response.Header.Set("Content-Type", "application/x-x509-ca-cert")
session.response.ContentLength = int64(len(b))
return session.writeResponse()
}
b := `<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>Clash MITM Proxy Services - 404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL %s was not found on this server.</p>
</body></html>
`
if opt.Handler.HandleApiRequest(session) {
return nil
}
b = fmt.Sprintf(b, session.request.URL.Path)
session.response = session.NewResponse(http.StatusNotFound, bytes.NewReader([]byte(b)))
session.response.Close = true
session.response.Header.Set("Content-Type", "text/html;charset=utf-8")
session.response.ContentLength = int64(len(b))
return session.writeResponse()
}
func handleError(opt *Option, session *Session, err error) {
if session.response != nil {
defer func() {
_, _ = io.Copy(io.Discard, session.response.Body)
_ = session.response.Body.Close()
}()
}
opt.Handler.HandleError(session, err)
}
func prepareRequest(connState *tls.ConnectionState, request *http.Request) {
host := request.Header.Get("Host")
if host != "" {
request.Host = host
}
if request.URL.Host == "" {
request.URL.Host = request.Host
}
if request.URL.Scheme == "" {
request.URL.Scheme = "http"
}
if connState != nil {
request.TLS = connState
request.URL.Scheme = "https"
}
if request.Header.Get("Accept-Encoding") != "" {
request.Header.Set("Accept-Encoding", "gzip")
}
}
func parseSourceAddress(req *http.Request, connSource, source net.Addr) net.Addr {
if source != nil {
return source
}
sourceAddress := req.Header.Get("Origin-Request-Source-Address")
if sourceAddress == "" {
return connSource
}
req.Header.Del("Origin-Request-Source-Address")
addrPort, err := netip.ParseAddrPort(sourceAddress)
if err != nil {
return connSource
}
return &net.TCPAddr{
IP: addrPort.Addr().AsSlice(),
Port: int(addrPort.Port()),
}
}
func isWebsocketRequest(req *http.Request) bool {
return strings.EqualFold(req.Header.Get("Connection"), "Upgrade") && strings.EqualFold(req.Header.Get("Upgrade"), "websocket")
}
func isHTTPTraffic(buf []byte) bool {
method, _, _ := strings.Cut(string(buf), " ")
return validMethod(method)
}

View File

@ -1,88 +0,0 @@
package mitm
import (
"crypto/tls"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/cert"
C "github.com/Dreamacro/clash/constant"
"net"
"net/http"
)
type Handler interface {
HandleRequest(*Session) (*http.Request, *http.Response) // Session.Response maybe nil
HandleResponse(*Session) *http.Response
HandleApiRequest(*Session) bool
HandleError(*Session, error) // Session maybe nil
}
type Option struct {
Addr string
ApiHost string
TLSConfig *tls.Config
CertConfig *cert.Config
Handler Handler
}
type Listener struct {
*Option
listener net.Listener
addr string
closed bool
}
// RawAddress implements C.Listener
func (l *Listener) RawAddress() string {
return l.addr
}
// Address implements C.Listener
func (l *Listener) Address() string {
return l.listener.Addr().String()
}
// Close implements C.Listener
func (l *Listener) Close() error {
l.closed = true
return l.listener.Close()
}
// New the MITM proxy actually is a type of HTTP proxy
func New(option *Option, in chan<- C.ConnContext) (*Listener, error) {
return NewWithAuthenticate(option, in, true)
}
func NewWithAuthenticate(option *Option, in chan<- C.ConnContext, authenticate bool) (*Listener, error) {
l, err := net.Listen("tcp", option.Addr)
if err != nil {
return nil, err
}
var c *cache.LruCache[string, bool]
if authenticate {
c = cache.New[string, bool](cache.WithAge[string, bool](90))
}
hl := &Listener{
listener: l,
addr: option.Addr,
Option: option,
}
go func() {
for {
conn, err1 := hl.listener.Accept()
if err1 != nil {
if hl.closed {
break
}
continue
}
go HandleConn(conn, option, in, c)
}
}()
return hl, nil
}

View File

@ -1,59 +0,0 @@
package mitm
import (
"io"
"net"
"net/http"
)
type Session struct {
conn net.Conn
request *http.Request
response *http.Response
props map[string]any
}
func (s *Session) Request() *http.Request {
return s.request
}
func (s *Session) Response() *http.Response {
return s.response
}
func (s *Session) GetProperties(key string) (any, bool) {
v, ok := s.props[key]
return v, ok
}
func (s *Session) SetProperties(key string, val any) {
s.props[key] = val
}
func (s *Session) NewResponse(code int, body io.Reader) *http.Response {
return NewResponse(code, body, s.request)
}
func (s *Session) NewErrorResponse(err error) *http.Response {
return NewErrorResponse(s.request, err)
}
func (s *Session) writeResponse() error {
if s.response == nil {
return ErrInvalidResponse
}
defer func(resp *http.Response) {
_ = resp.Body.Close()
}(s.response)
return s.response.Write(s.conn)
}
func newSession(conn net.Conn, request *http.Request, response *http.Response) *Session {
return &Session{
conn: conn,
request: request,
response: response,
props: map[string]any{},
}
}

View File

@ -1,95 +0,0 @@
package mitm
import (
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
"golang.org/x/text/encoding/charmap"
"golang.org/x/text/transform"
)
var (
ErrInvalidResponse = errors.New("invalid response")
ErrInvalidURL = errors.New("invalid URL")
)
func NewResponse(code int, body io.Reader, req *http.Request) *http.Response {
if body == nil {
body = &bytes.Buffer{}
}
rc, ok := body.(io.ReadCloser)
if !ok {
rc = ioutil.NopCloser(body)
}
res := &http.Response{
StatusCode: code,
Status: fmt.Sprintf("%d %s", code, http.StatusText(code)),
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: http.Header{},
Body: rc,
Request: req,
}
if req != nil {
res.Close = req.Close
res.Proto = req.Proto
res.ProtoMajor = req.ProtoMajor
res.ProtoMinor = req.ProtoMinor
}
return res
}
func NewErrorResponse(req *http.Request, err error) *http.Response {
res := NewResponse(http.StatusBadGateway, nil, req)
res.Close = true
date := res.Header.Get("Date")
if date == "" {
date = time.Now().Format(http.TimeFormat)
}
w := fmt.Sprintf(`199 "clash" %q %q`, err.Error(), date)
res.Header.Add("Warning", w)
return res
}
func ReadDecompressedBody(res *http.Response) ([]byte, error) {
rBody := res.Body
if res.Header.Get("Content-Encoding") == "gzip" {
gzReader, err := gzip.NewReader(rBody)
if err != nil {
return nil, err
}
rBody = gzReader
defer func(gzReader *gzip.Reader) {
_ = gzReader.Close()
}(gzReader)
}
return ioutil.ReadAll(rBody)
}
func DecodeLatin1(reader io.Reader) (string, error) {
r := transform.NewReader(reader, charmap.ISO8859_1.NewDecoder())
b, err := ioutil.ReadAll(r)
if err != nil {
return "", err
}
return string(b), nil
}
func EncodeLatin1(str string) ([]byte, error) {
return charmap.ISO8859_1.NewEncoder().Bytes([]byte(str))
}

View File

@ -1,9 +1,9 @@
package mixed
import (
"github.com/Dreamacro/clash/adapter/inbound"
"net"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/cache"
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
@ -70,7 +70,7 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*
}
func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool], additions ...inbound.Addition) {
N.TCPKeepAlive(conn)
conn.(*net.TCPConn).SetKeepAlive(true)
bufConn := N.NewBufferedConn(conn)
head, err := bufConn.Peek(1)

View File

@ -4,7 +4,6 @@ import (
"net"
"github.com/Dreamacro/clash/adapter/inbound"
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
)
@ -67,6 +66,6 @@ func handleRedir(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Ad
conn.Close()
return
}
N.TCPKeepAlive(conn)
conn.(*net.TCPConn).SetKeepAlive(true)
in <- inbound.NewSocket(target, conn, C.REDIR, additions...)
}

View File

@ -59,7 +59,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C
}
continue
}
N.TCPKeepAlive(c)
_ = c.(*net.TCPConn).SetKeepAlive(true)
go sl.HandleConn(c, tcpIn)
}
}()

View File

@ -72,27 +72,7 @@ func UpstreamMetadata(metadata M.Metadata) M.Metadata {
}
}
func ConvertMetadata(metadata *C.Metadata) M.Metadata {
return M.Metadata{
Protocol: metadata.Type.String(),
Source: M.SocksaddrFrom(metadata.SrcIP, metadata.SrcPort),
Destination: M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort),
}
}
func (h *ListenerHandler) IsSpecialFqdn(fqdn string) bool {
switch fqdn {
case mux.Destination.Fqdn:
case vmess.MuxDestination.Fqdn:
case uot.MagicAddress:
case uot.LegacyMagicAddress:
default:
return false
}
return true
}
func (h *ListenerHandler) ParseSpecialFqdn(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
switch metadata.Destination.Fqdn {
case mux.Destination.Fqdn:
return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata))
@ -109,13 +89,6 @@ func (h *ListenerHandler) ParseSpecialFqdn(ctx context.Context, conn net.Conn, m
metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()}
return h.NewPacketConnection(ctx, uot.NewConn(conn, uot.Request{}), metadata)
}
return errors.New("not special fqdn")
}
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
if h.IsSpecialFqdn(metadata.Destination.Fqdn) {
return h.ParseSpecialFqdn(ctx, conn, metadata)
}
target := socks5.ParseAddr(metadata.Destination.String())
wg := &sync.WaitGroup{}
defer wg.Wait() // this goroutine must exit after conn.Close()

View File

@ -5,16 +5,15 @@ import (
"fmt"
"net"
"strings"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
N "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/sockopt"
C "github.com/Dreamacro/clash/constant"
LC "github.com/Dreamacro/clash/listener/config"
embedSS "github.com/Dreamacro/clash/listener/shadowsocks"
"github.com/Dreamacro/clash/listener/sing"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/ntp"
shadowsocks "github.com/metacubex/sing-shadowsocks"
"github.com/metacubex/sing-shadowsocks/shadowaead"
@ -65,7 +64,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C
case common.Contains(shadowaead.List, config.Cipher):
sl.service, err = shadowaead.NewService(config.Cipher, nil, config.Password, udpTimeout, h)
case common.Contains(shadowaead_2022.List, config.Cipher):
sl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h, ntp.Now)
sl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h, time.Now)
default:
err = fmt.Errorf("shadowsocks: unsupported method: %s", config.Cipher)
return embedSS.New(config, tcpIn, udpIn)
@ -146,7 +145,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C
}
continue
}
N.TCPKeepAlive(c)
_ = c.(*net.TCPConn).SetKeepAlive(true)
go sl.HandleConn(c, tcpIn)
}

View File

@ -7,11 +7,9 @@ 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/listener/sing"
"github.com/Dreamacro/clash/ntp"
vmess "github.com/metacubex/sing-vmess"
"github.com/sagernet/sing/common"
@ -44,7 +42,7 @@ func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packe
Additions: additions,
}
service := vmess.NewService[string](h, vmess.ServiceWithDisableHeaderProtection(), vmess.ServiceWithTimeFunc(ntp.Now))
service := vmess.NewService[string](h, vmess.ServiceWithDisableHeaderProtection())
err = service.UpdateUsers(
common.Map(config.Users, func(it LC.VmessUser) string {
return it.Username
@ -85,7 +83,7 @@ func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packe
}
continue
}
N.TCPKeepAlive(c)
_ = c.(*net.TCPConn).SetKeepAlive(true)
go sl.HandleConn(c, tcpIn)
}

View File

@ -67,7 +67,7 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*
}
func handleSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
N.TCPKeepAlive(conn)
conn.(*net.TCPConn).SetKeepAlive(true)
bufConn := N.NewBufferedConn(conn)
head, err := bufConn.Peek(1)
if err != nil {

View File

@ -55,15 +55,16 @@ func (c *packet) InAddr() net.Addr {
func createOrGetLocalConn(rAddr, lAddr net.Addr, in chan<- C.PacketAdapter, natTable C.NatTable) (*net.UDPConn, error) {
remote := rAddr.String()
local := lAddr.String()
localConn := natTable.GetForLocalConn(local, remote)
localConn := natTable.GetLocalConn(local, remote)
// localConn not exist
if localConn == nil {
cond, loaded := natTable.GetOrCreateLockForLocalConn(local, remote)
lockKey := remote + "-lock"
cond, loaded := natTable.GetOrCreateLockForLocalConn(local, lockKey)
if loaded {
cond.L.Lock()
cond.Wait()
// we should get localConn here
localConn = natTable.GetForLocalConn(local, remote)
localConn = natTable.GetLocalConn(local, remote)
if localConn == nil {
return nil, fmt.Errorf("localConn is nil, nat entry not exist")
}
@ -73,7 +74,7 @@ func createOrGetLocalConn(rAddr, lAddr net.Addr, in chan<- C.PacketAdapter, natT
return nil, fmt.Errorf("cond is nil, nat entry not exist")
}
defer func() {
natTable.DeleteLockForLocalConn(local, remote)
natTable.DeleteLocalConnMap(local, lockKey)
cond.Broadcast()
}()
conn, err := listenLocalConn(rAddr, lAddr, in, natTable)
@ -81,7 +82,7 @@ func createOrGetLocalConn(rAddr, lAddr net.Addr, in chan<- C.PacketAdapter, natT
log.Errorln("listenLocalConn failed with error: %s, packet loss (rAddr[%T]=%s lAddr[%T]=%s)", err.Error(), rAddr, remote, lAddr, local)
return nil, err
}
natTable.AddForLocalConn(local, remote, conn)
natTable.AddLocalConn(local, remote, conn)
localConn = conn
}
}

View File

@ -4,7 +4,6 @@ import (
"net"
"github.com/Dreamacro/clash/adapter/inbound"
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5"
)
@ -33,7 +32,7 @@ func (l *Listener) Close() error {
func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
target := socks5.ParseAddrToSocksAddr(conn.LocalAddr())
N.TCPKeepAlive(conn)
conn.(*net.TCPConn).SetKeepAlive(true)
in <- inbound.NewSocket(target, conn, C.TPROXY, additions...)
}

View File

@ -1,7 +1,6 @@
package tuic
import (
"context"
"crypto/tls"
"net"
"strings"
@ -12,7 +11,6 @@ import (
"github.com/Dreamacro/clash/common/sockopt"
C "github.com/Dreamacro/clash/constant"
LC "github.com/Dreamacro/clash/listener/config"
"github.com/Dreamacro/clash/listener/sing"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5"
"github.com/Dreamacro/clash/transport/tuic"
@ -38,12 +36,6 @@ func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packet
inbound.WithSpecialRules(""),
}
}
h := &sing.ListenerHandler{
TcpIn: tcpIn,
UdpIn: udpIn,
Type: C.TUIC,
Additions: additions,
}
cert, err := CN.ParseCert(config.Certificate, config.PrivateKey)
if err != nil {
return nil, err
@ -94,19 +86,7 @@ func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packet
newAdditions = slices.Clone(additions)
newAdditions = append(newAdditions, _additions...)
}
connCtx := inbound.NewSocket(addr, conn, C.TUIC, newAdditions...)
metadata := sing.ConvertMetadata(connCtx.Metadata())
if h.IsSpecialFqdn(metadata.Destination.Fqdn) {
go func() { // ParseSpecialFqdn will block, so open a new goroutine
_ = h.ParseSpecialFqdn(
sing.WithAdditions(context.Background(), newAdditions...),
conn,
metadata,
)
}()
return nil
}
tcpIn <- connCtx
tcpIn <- inbound.NewSocket(addr, conn, C.TUIC, newAdditions...)
return nil
}
handleUdpFn := func(addr socks5.Addr, packet C.UDPPacket, _additions ...inbound.Addition) error {

View File

@ -5,7 +5,6 @@ import (
"net"
"github.com/Dreamacro/clash/adapter/inbound"
N "github.com/Dreamacro/clash/common/net"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5"
)
@ -35,7 +34,7 @@ func (l *Listener) Close() error {
}
func (l *Listener) handleTCP(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
N.TCPKeepAlive(conn)
conn.(*net.TCPConn).SetKeepAlive(true)
ctx := inbound.NewSocket(l.target, conn, C.TUNNEL, additions...)
ctx.Metadata().SpecialProxy = l.proxy
in <- ctx

View File

@ -1,95 +0,0 @@
package ntp
import (
"context"
"github.com/Dreamacro/clash/log"
"github.com/beevik/ntp"
"sync"
"time"
)
var offset time.Duration
var service *Service
type Service struct {
addr string
interval time.Duration
ticker *time.Ticker
ctx context.Context
cancel context.CancelFunc
mu sync.Mutex
running bool
}
func ReCreateNTPService(addr string, interval time.Duration) {
if service != nil {
service.Stop()
}
ctx, cancel := context.WithCancel(context.Background())
service = &Service{addr: addr, interval: interval, ctx: ctx, cancel: cancel}
service.Start()
}
func (srv *Service) Start() {
srv.mu.Lock()
defer srv.mu.Unlock()
log.Infoln("NTP service start")
srv.ticker = time.NewTicker(srv.interval * time.Minute)
service.running = true
go func() {
for {
err := srv.updateTime(srv.addr)
if err != nil {
log.Warnln("updateTime failed: %s", err)
}
select {
case <-srv.ticker.C:
case <-srv.ctx.Done():
return
}
}
}()
}
func (srv *Service) Stop() {
srv.mu.Lock()
defer srv.mu.Unlock()
if service.running {
srv.ticker.Stop()
srv.cancel()
service.running = false
}
}
func (srv *Service) Running() bool {
if srv == nil {
return false
}
srv.mu.Lock()
defer srv.mu.Unlock()
return srv.running
}
func (srv *Service) updateTime(addr string) error {
response, err := ntp.Query(addr)
if err != nil {
return err
}
localTime := time.Now()
ntpTime := response.Time
offset = localTime.Sub(ntpTime)
if offset > time.Duration(0) {
log.Warnln("System clock is ahead of NTP time by %s", offset)
} else if offset < time.Duration(0) {
log.Warnln("System clock is behind NTP time by %s", -offset)
}
return nil
}
func Now() time.Time {
now := time.Now()
if service.Running() && offset.Abs() > 0 {
now = now.Add(offset)
}
return now
}

View File

@ -1,72 +0,0 @@
package rewrites
import (
"bytes"
"io"
"io/ioutil"
C "github.com/Dreamacro/clash/constant"
)
var (
EmptyDict = NewResponseBody([]byte("{}"))
EmptyArray = NewResponseBody([]byte("[]"))
OnePixelPNG = NewResponseBody([]byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00, 0x11, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x62, 0x60, 0x60, 0x60, 0x00, 0x04, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x00, 0x03, 0xfe, 0x8f, 0xeb, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82})
)
type Body interface {
Body() io.ReadCloser
ContentLength() int64
}
type ResponseBody struct {
data []byte
length int64
}
func (r *ResponseBody) Body() io.ReadCloser {
return ioutil.NopCloser(bytes.NewReader(r.data))
}
func (r *ResponseBody) ContentLength() int64 {
return r.length
}
func NewResponseBody(data []byte) *ResponseBody {
return &ResponseBody{
data: data,
length: int64(len(data)),
}
}
type RewriteRules struct {
request []C.Rewrite
response []C.Rewrite
}
func (rr *RewriteRules) SearchInRequest(do func(C.Rewrite) bool) bool {
for _, v := range rr.request {
if do(v) {
return true
}
}
return false
}
func (rr *RewriteRules) SearchInResponse(do func(C.Rewrite) bool) bool {
for _, v := range rr.response {
if do(v) {
return true
}
}
return false
}
func NewRewriteRules(req []C.Rewrite, res []C.Rewrite) *RewriteRules {
return &RewriteRules{
request: req,
response: res,
}
}
var _ C.RewriteRule = (*RewriteRules)(nil)

View File

@ -1,212 +0,0 @@
package rewrites
import (
"bufio"
"bytes"
"errors"
log "github.com/sirupsen/logrus"
"io"
"net/http"
"net/textproto"
"strconv"
"strings"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/mitm"
"github.com/Dreamacro/clash/tunnel"
)
var _ mitm.Handler = (*RewriteHandler)(nil)
type RewriteHandler struct{}
func (*RewriteHandler) HandleRequest(session *mitm.Session) (*http.Request, *http.Response) {
var (
request = session.Request()
response *http.Response
)
rule, sub, found := matchRewriteRule(request.URL.String(), true)
if !found {
return nil, nil
}
log.Infof("[MITM] %s <- request %s", rule.RuleType().String(), request.URL.String())
switch rule.RuleType() {
case C.MitmReject:
response = session.NewResponse(http.StatusNotFound, nil)
response.Header.Set("Content-Type", "text/html; charset=utf-8")
case C.MitmReject200:
response = session.NewResponse(http.StatusOK, nil)
response.Header.Set("Content-Type", "text/html; charset=utf-8")
case C.MitmRejectImg:
response = session.NewResponse(http.StatusOK, OnePixelPNG.Body())
response.Header.Set("Content-Type", "image/png")
response.ContentLength = OnePixelPNG.ContentLength()
case C.MitmRejectDict:
response = session.NewResponse(http.StatusOK, EmptyDict.Body())
response.Header.Set("Content-Type", "application/json; charset=utf-8")
response.ContentLength = EmptyDict.ContentLength()
case C.MitmRejectArray:
response = session.NewResponse(http.StatusOK, EmptyArray.Body())
response.Header.Set("Content-Type", "application/json; charset=utf-8")
response.ContentLength = EmptyArray.ContentLength()
case C.Mitm302:
response = session.NewResponse(http.StatusFound, nil)
response.Header.Set("Location", rule.ReplaceURLPayload(sub))
case C.Mitm307:
response = session.NewResponse(http.StatusTemporaryRedirect, nil)
response.Header.Set("Location", rule.ReplaceURLPayload(sub))
case C.MitmRequestHeader:
if len(request.Header) == 0 {
return nil, nil
}
rawHeader := &bytes.Buffer{}
oldHeader := request.Header
if err := oldHeader.Write(rawHeader); err != nil {
return nil, nil
}
newRawHeader := rule.ReplaceSubPayload(rawHeader.String())
tb := textproto.NewReader(bufio.NewReader(strings.NewReader(newRawHeader)))
newHeader, err := tb.ReadMIMEHeader()
if err != nil && !errors.Is(err, io.EOF) {
return nil, nil
}
request.Header = http.Header(newHeader)
case C.MitmRequestBody:
if !CanRewriteBody(request.ContentLength, request.Header.Get("Content-Type")) {
return nil, nil
}
buf := make([]byte, request.ContentLength)
_, err := io.ReadFull(request.Body, buf)
if err != nil {
return nil, nil
}
newBody := rule.ReplaceSubPayload(string(buf))
request.Body = io.NopCloser(strings.NewReader(newBody))
request.ContentLength = int64(len(newBody))
default:
found = false
}
if found {
if response != nil {
response.Close = true
}
return request, response
}
return nil, nil
}
func (*RewriteHandler) HandleResponse(session *mitm.Session) *http.Response {
var (
request = session.Request()
response = session.Response()
)
rule, _, found := matchRewriteRule(request.URL.String(), false)
found = found && rule.RuleRegx() != nil
if !found {
return nil
}
log.Infof("[MITM] %s <- response %s", rule.RuleType().String(), request.URL.String())
switch rule.RuleType() {
case C.MitmResponseHeader:
if len(response.Header) == 0 {
return nil
}
rawHeader := &bytes.Buffer{}
oldHeader := response.Header
if err := oldHeader.Write(rawHeader); err != nil {
return nil
}
newRawHeader := rule.ReplaceSubPayload(rawHeader.String())
tb := textproto.NewReader(bufio.NewReader(strings.NewReader(newRawHeader)))
newHeader, err := tb.ReadMIMEHeader()
if err != nil && !errors.Is(err, io.EOF) {
return nil
}
response.Header = http.Header(newHeader)
response.Header.Set("Content-Length", strconv.FormatInt(response.ContentLength, 10))
case C.MitmResponseBody:
if !CanRewriteBody(response.ContentLength, response.Header.Get("Content-Type")) {
return nil
}
b, err := mitm.ReadDecompressedBody(response)
_ = response.Body.Close()
if err != nil {
return nil
}
//body, err := mitm.DecodeLatin1(bytes.NewReader(b))
//if err != nil {
// return nil
//}
newBody := rule.ReplaceSubPayload(string(b))
//modifiedBody, err := mitm.EncodeLatin1(newBody)
//if err != nil {
// return nil
//}
modifiedBody := []byte(newBody)
response.Body = io.NopCloser(bytes.NewReader(modifiedBody))
response.Header.Del("Content-Encoding")
response.ContentLength = int64(len(modifiedBody))
default:
found = false
}
if found {
return response
}
return nil
}
func (h *RewriteHandler) HandleApiRequest(*mitm.Session) bool {
return false
}
// HandleError session maybe nil
func (h *RewriteHandler) HandleError(*mitm.Session, error) {}
func matchRewriteRule(url string, isRequest bool) (rr C.Rewrite, sub []string, found bool) {
rewrites := tunnel.Rewrites()
if isRequest {
found = rewrites.SearchInRequest(func(r C.Rewrite) bool {
sub, err := r.URLRegx().FindStringMatch(url)
if err != nil || sub == nil {
return false
}
rr = r
var groups []string
for _, fg := range sub.Groups() {
groups = append(groups, fg.String())
}
return true
})
} else {
found = rewrites.SearchInResponse(func(r C.Rewrite) bool {
if b, err := r.URLRegx().MatchString(url); b && err == nil {
rr = r
return true
}
return false
})
}
return
}

View File

@ -1,53 +0,0 @@
package rewrites
import (
regexp "github.com/dlclark/regexp2"
"strings"
C "github.com/Dreamacro/clash/constant"
)
func ParseRewrite(line RawMitmRule) (C.Rewrite, error) {
var (
urlRegx *regexp.Regexp
ruleType *C.RewriteType
ruleRegx *regexp.Regexp
rulePayload string
err error
)
url := line.Url
urlRegx, err = regexp.Compile(strings.Trim(url, " "), regexp.None)
if err != nil {
return nil, err
}
ruleType = &line.Action
switch *ruleType {
case C.Mitm302, C.Mitm307:
{
rulePayload = line.New
break
}
case C.MitmRequestHeader, C.MitmRequestBody, C.MitmResponseHeader, C.MitmResponseBody:
{
var old string
if line.Old == nil {
old = ".*"
} else {
old = *line.Old
}
re, err := regexp.Compile(old, regexp.Singleline)
if err != nil {
return nil, err
}
ruleRegx = re
rulePayload = line.New
}
}
return NewRewriteRule(urlRegx, *ruleType, ruleRegx, rulePayload), nil
}

View File

@ -1,105 +0,0 @@
package rewrites
import (
regexp "github.com/dlclark/regexp2"
"strconv"
"strings"
C "github.com/Dreamacro/clash/constant"
"github.com/gofrs/uuid"
)
type RawMitmRule struct {
Url string `yaml:"url" json:"url"`
Action C.RewriteType `yaml:"action" json:"action"`
Old *string `yaml:"old" json:"old"`
New string `yaml:"new" json:"new"`
}
type RewriteRule struct {
id string
urlRegx *regexp.Regexp
ruleType C.RewriteType
ruleRegx *regexp.Regexp
rulePayload string
}
func (r *RewriteRule) ID() string {
return r.id
}
func (r *RewriteRule) URLRegx() *regexp.Regexp {
return r.urlRegx
}
func (r *RewriteRule) RuleType() C.RewriteType {
return r.ruleType
}
func (r *RewriteRule) RuleRegx() *regexp.Regexp {
return r.ruleRegx
}
func (r *RewriteRule) RulePayload() string {
return r.rulePayload
}
func (r *RewriteRule) ReplaceURLPayload(matchSub []string) string {
url := r.rulePayload
l := len(matchSub)
if l < 2 {
return url
}
for i := 1; i < l; i++ {
url = strings.ReplaceAll(url, "$"+strconv.Itoa(i), matchSub[i])
}
return url
}
func (r *RewriteRule) ReplaceSubPayload(oldData string) string {
payload := r.rulePayload
if r.ruleRegx == nil {
return oldData
}
sub, err := r.ruleRegx.FindStringMatch(oldData)
for err == nil && sub != nil {
var (
groups []string
sPayload = payload
)
for _, fg := range sub.Groups() {
groups = append(groups, fg.String())
}
l := len(groups)
for i := 1; i < l; i++ {
sPayload = strings.Replace(payload, "$"+strconv.Itoa(i), groups[i], 1)
}
oldData = strings.Replace(oldData, groups[0], sPayload, 1)
sub, err = r.ruleRegx.FindNextMatch(sub)
}
return oldData
}
func NewRewriteRule(urlRegx *regexp.Regexp, ruleType C.RewriteType, ruleRegx *regexp.Regexp, rulePayload string) *RewriteRule {
id, _ := uuid.NewV4()
return &RewriteRule{
id: id.String(),
urlRegx: urlRegx,
ruleType: ruleType,
ruleRegx: ruleRegx,
rulePayload: rulePayload,
}
}
var _ C.Rewrite = (*RewriteRule)(nil)

View File

@ -1,28 +0,0 @@
package rewrites
import (
"strings"
)
var allowContentType = []string{
"text/",
"application/xhtml",
"application/xml",
"application/atom+xml",
"application/json",
"application/x-www-form-urlencoded",
}
func CanRewriteBody(contentLength int64, contentType string) bool {
if contentLength <= 0 {
return false
}
for _, v := range allowContentType {
if strings.HasPrefix(contentType, v) {
return true
}
}
return false
}

View File

@ -1,52 +0,0 @@
package common
import (
"strings"
C "github.com/Dreamacro/clash/constant"
)
type UserAgent struct {
*Base
ua string
adapter string
}
func (d *UserAgent) RuleType() C.RuleType {
return C.UserAgent
}
func (d *UserAgent) Match(metadata *C.Metadata) (bool, string) {
if metadata.Type != C.MITM || metadata.UserAgent == "" {
return false, d.adapter
}
return strings.Contains(metadata.UserAgent, d.ua), d.adapter
}
func (d *UserAgent) Adapter() string {
return d.adapter
}
func (d *UserAgent) Payload() string {
return d.ua
}
func (d *UserAgent) ShouldResolveIP() bool {
return false
}
func NewUserAgent(ua string, adapter string) (*UserAgent, error) {
ua = strings.Trim(ua, "*")
if ua == "" {
return nil, errPayload
}
return &UserAgent{
Base: &Base{},
ua: ua,
adapter: adapter,
}, nil
}
var _ C.Rule = (*UserAgent)(nil)

View File

@ -14,13 +14,12 @@ require (
replace github.com/Dreamacro/clash => ../
require (
github.com/3andne/restls-client-go v0.1.6 // indirect
github.com/3andne/restls-client-go v0.1.4 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/RyuaNerin/go-krypto v1.0.2 // indirect
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/beevik/ntp v1.3.0 // indirect
github.com/cilium/ebpf v0.11.0 // indirect
github.com/coreos/go-iptables v0.7.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@ -32,7 +31,6 @@ require (
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/gaukas/godicttls v0.0.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gofrs/uuid/v5 v5.0.0 // indirect
@ -46,7 +44,7 @@ require (
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
@ -54,7 +52,7 @@ require (
github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
github.com/metacubex/gvisor v0.0.0-20230611153922-78842f086475 // indirect
github.com/metacubex/quic-go v0.38.1-0.20230821081539-517fdb17fb28 // indirect
github.com/metacubex/quic-go v0.37.4-0.20230806014204-ef9b221eec12 // indirect
github.com/metacubex/sing-shadowsocks v0.2.4 // indirect
github.com/metacubex/sing-shadowsocks2 v0.1.3 // indirect
github.com/metacubex/sing-tun v0.1.11 // indirect
@ -62,7 +60,7 @@ require (
github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mroth/weightedrand/v2 v2.1.0 // indirect
github.com/mroth/weightedrand/v2 v2.0.2 // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/openacid/low v0.1.21 // indirect
@ -73,18 +71,17 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/puzpuzpuz/xsync/v2 v2.5.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.3.1 // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a // indirect
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c // indirect
github.com/sagernet/sing v0.2.9 // indirect
github.com/sagernet/sing-mux v0.1.2 // indirect
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f // indirect
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
github.com/shirou/gopsutil/v3 v3.23.7 // indirect
@ -97,30 +94,19 @@ require (
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
github.com/zhangyunhao116/fastrand v0.3.0 // indirect
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb // indirect
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.9.1 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6 // indirect
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.53.0-dev.0.20230123225046-4075ef07c5d5 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.4.0 // indirect
gvisor.dev/gvisor v0.0.0-20220326024801-5d1f3d24cb84 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
)

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,10 @@
package gun
func UVarintLen(x uint64) int {
switch {
case x < 1<<(7*1):
return 1
case x < 1<<(7*2):
return 2
case x < 1<<(7*3):
return 3
case x < 1<<(7*4):
return 4
case x < 1<<(7*5):
return 5
case x < 1<<(7*6):
return 6
case x < 1<<(7*7):
return 7
case x < 1<<(7*8):
return 8
case x < 1<<(7*9):
return 9
default:
return 10
i := 0
for x >= 0x80 {
x >>= 7
i++
}
return i + 1
}

View File

@ -22,7 +22,6 @@ import (
"github.com/Dreamacro/clash/transport/tuic/common"
"github.com/metacubex/quic-go"
"github.com/puzpuzpuz/xsync/v2"
"github.com/zhangyunhao116/fastrand"
)
@ -50,7 +49,7 @@ type clientImpl struct {
openStreams atomic.Int64
closed atomic.Bool
udpInputMap *xsync.MapOf[uint32, net.Conn]
udpInputMap sync.Map
// only ready for PoolClient
dialerRef C.Dialer
@ -264,10 +263,11 @@ func (t *clientImpl) forceClose(quicConn quic.Connection, err error) {
if quicConn != nil {
_ = quicConn.CloseWithError(ProtocolError, errStr)
}
udpInputMap := t.udpInputMap
udpInputMap.Range(func(key uint32, value net.Conn) bool {
conn := value
_ = conn.Close()
udpInputMap := &t.udpInputMap
udpInputMap.Range(func(key, value any) bool {
if conn, ok := value.(net.Conn); ok {
_ = conn.Close()
}
udpInputMap.Delete(key)
return true
})
@ -469,7 +469,6 @@ func NewClient(clientOption *ClientOption, udp bool, dialerRef C.Dialer) *Client
ClientOption: clientOption,
udp: udp,
dialerRef: dialerRef,
udpInputMap: xsync.NewIntegerMapOf[uint32, net.Conn](),
}
c := &Client{ci}
runtime.SetFinalizer(c, closeClient)

View File

@ -17,7 +17,6 @@ import (
"github.com/gofrs/uuid/v5"
"github.com/metacubex/quic-go"
"github.com/puzpuzpuz/xsync/v2"
)
type ServerOption struct {
@ -34,7 +33,6 @@ func NewServerHandler(option *ServerOption, quicConn quic.EarlyConnection, uuid
quicConn: quicConn,
uuid: uuid,
authCh: make(chan struct{}),
udpInputMap: xsync.NewIntegerMapOf[uint32, *atomic.Bool](),
}
}
@ -47,7 +45,7 @@ type serverHandler struct {
authOk atomic.Bool
authOnce sync.Once
udpInputMap *xsync.MapOf[uint32, *atomic.Bool]
udpInputMap sync.Map
}
func (s *serverHandler) AuthOk() bool {
@ -80,7 +78,8 @@ func (s *serverHandler) parsePacket(packet *Packet, udpRelayMode common.UdpRelay
assocId = packet.ASSOC_ID
writeClosed, _ := s.udpInputMap.LoadOrCompute(assocId, func() *atomic.Bool { return &atomic.Bool{} })
v, _ := s.udpInputMap.LoadOrStore(assocId, &atomic.Bool{})
writeClosed := v.(*atomic.Bool)
if writeClosed.Load() {
return nil
}
@ -174,7 +173,8 @@ func (s *serverHandler) HandleUniStream(reader *bufio.Reader) (err error) {
if err != nil {
return
}
if writeClosed, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded {
if v, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded {
writeClosed := v.(*atomic.Bool)
writeClosed.Store(true)
}
case HeartbeatType:

View File

@ -20,7 +20,6 @@ import (
"github.com/Dreamacro/clash/transport/tuic/common"
"github.com/metacubex/quic-go"
"github.com/puzpuzpuz/xsync/v2"
"github.com/zhangyunhao116/fastrand"
)
@ -47,7 +46,7 @@ type clientImpl struct {
openStreams atomic.Int64
closed atomic.Bool
udpInputMap xsync.MapOf[uint16, net.Conn]
udpInputMap sync.Map
// only ready for PoolClient
dialerRef C.Dialer
@ -271,9 +270,10 @@ func (t *clientImpl) forceClose(quicConn quic.Connection, err error) {
_ = quicConn.CloseWithError(ProtocolError, errStr)
}
udpInputMap := &t.udpInputMap
udpInputMap.Range(func(key uint16, value net.Conn) bool {
conn := value
_ = conn.Close()
udpInputMap.Range(func(key, value any) bool {
if conn, ok := value.(net.Conn); ok {
_ = conn.Close()
}
udpInputMap.Delete(key)
return true
})
@ -406,7 +406,6 @@ func NewClient(clientOption *ClientOption, udp bool, dialerRef C.Dialer) *Client
ClientOption: clientOption,
udp: udp,
dialerRef: dialerRef,
udpInputMap: *xsync.NewIntegerMapOf[uint16, net.Conn](),
}
c := &Client{ci}
runtime.SetFinalizer(c, closeClient)

View File

@ -16,7 +16,6 @@ import (
"github.com/gofrs/uuid/v5"
"github.com/metacubex/quic-go"
"github.com/puzpuzpuz/xsync/v2"
)
type ServerOption struct {
@ -33,7 +32,6 @@ func NewServerHandler(option *ServerOption, quicConn quic.EarlyConnection, uuid
quicConn: quicConn,
uuid: uuid,
authCh: make(chan struct{}),
udpInputMap: xsync.NewIntegerMapOf[uint16, *serverUDPInput](),
}
}
@ -47,7 +45,7 @@ type serverHandler struct {
authUUID atomic.TypedValue[string]
authOnce sync.Once
udpInputMap *xsync.MapOf[uint16, *serverUDPInput]
udpInputMap sync.Map
}
func (s *serverHandler) AuthOk() bool {
@ -96,7 +94,8 @@ func (s *serverHandler) parsePacket(packet *Packet, udpRelayMode common.UdpRelay
assocId = packet.ASSOC_ID
input, _ := s.udpInputMap.LoadOrCompute(assocId, func() *serverUDPInput { return &serverUDPInput{} })
v, _ := s.udpInputMap.LoadOrStore(assocId, &serverUDPInput{})
input := v.(*serverUDPInput)
if input.writeClosed.Load() {
return nil
}
@ -187,7 +186,8 @@ func (s *serverHandler) HandleUniStream(reader *bufio.Reader) (err error) {
if err != nil {
return
}
if input, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded {
if v, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded {
input := v.(*serverUDPInput)
input.writeClosed.Store(true)
}
}

View File

@ -73,9 +73,12 @@ func handleUDPToLocal(writeBack C.WriteBack, pc N.EnhancePacketConn, key string,
}
func closeAllLocalCoon(lAddr string) {
natTable.RangeForLocalConn(lAddr, func(key string, value *net.UDPConn) bool {
conn := value
natTable.RangeLocalConn(lAddr, func(key, value any) bool {
conn, ok := value.(*net.UDPConn)
if !ok || conn == nil {
log.Debugln("Value %#v unknown value when closing TProxy local conn...", conn)
return true
}
conn.Close()
log.Debugln("Closing TProxy local conn... lAddr=%s rAddr=%s", lAddr, key)
return true

View File

@ -2,11 +2,11 @@ package statistic
import (
"os"
"sync"
"time"
"github.com/Dreamacro/clash/common/atomic"
"github.com/puzpuzpuz/xsync/v2"
"github.com/shirou/gopsutil/v3/process"
)
@ -14,7 +14,6 @@ var DefaultManager *Manager
func init() {
DefaultManager = &Manager{
connections: xsync.NewMapOf[Tracker](),
uploadTemp: atomic.NewInt64(0),
downloadTemp: atomic.NewInt64(0),
uploadBlip: atomic.NewInt64(0),
@ -28,7 +27,7 @@ func init() {
}
type Manager struct {
connections *xsync.MapOf[string, Tracker]
connections sync.Map
uploadTemp *atomic.Int64
downloadTemp *atomic.Int64
uploadBlip *atomic.Int64
@ -49,14 +48,14 @@ func (m *Manager) Leave(c Tracker) {
func (m *Manager) Get(id string) (c Tracker) {
if value, ok := m.connections.Load(id); ok {
c = value
c = value.(Tracker)
}
return
}
func (m *Manager) Range(f func(c Tracker) bool) {
m.connections.Range(func(key string, value Tracker) bool {
return f(value)
m.connections.Range(func(key, value any) bool {
return f(value.(Tracker))
})
}

View File

@ -30,7 +30,6 @@ var (
udpQueue = make(chan C.PacketAdapter, 200)
natTable = nat.New()
rules []C.Rule
rewrites C.RewriteRule
listeners = make(map[string]C.InboundListener)
subRules map[string][]C.Rule
proxies = make(map[string]C.Proxy)
@ -180,18 +179,6 @@ func isHandle(t C.Type) bool {
return status == Running || (status == Inner && t == C.INNER)
}
// Rewrites return all rewrites
func Rewrites() C.RewriteRule {
return rewrites
}
// UpdateRewrites handle update rewrites
func UpdateRewrites(rules C.RewriteRule) {
configMux.Lock()
rewrites = rules
configMux.Unlock()
}
// processUDP starts a loop to handle udp packet
func processUDP() {
queue := udpQueue
@ -331,7 +318,8 @@ func handleUDPConn(packet C.PacketAdapter) {
return
}
cond, loaded := natTable.GetOrCreateLock(key)
lockKey := key + "-lock"
cond, loaded := natTable.GetOrCreateLock(lockKey)
go func() {
defer packet.Drop()
@ -345,7 +333,7 @@ func handleUDPConn(packet C.PacketAdapter) {
}
defer func() {
natTable.DeleteLock(key)
natTable.Delete(lockKey)
cond.Broadcast()
}()
@ -446,9 +434,8 @@ func handleTCPConn(connCtx C.ConnContext) {
return
}
isMitmProxy := metadata.Type == C.MITM
dialMetadata := metadata
if len(metadata.Host) > 0 && !isMitmProxy {
if len(metadata.Host) > 0 {
if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok {
if dstIp, _ := node.RandIP(); !FakeIPRange().Contains(dstIp) {
dialMetadata.DstIP = dstIp
@ -561,10 +548,6 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
}
for _, rule := range getRules(metadata) {
if metadata.Type == C.MITM && rule.Adapter() == "MITM" {
continue
}
if !resolved && shouldResolveIP(rule, metadata) {
func() {
ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)