Compare commits
64 Commits
Meta
...
android-op
Author | SHA1 | Date | |
---|---|---|---|
![]() |
80fabce99c | ||
![]() |
347e606853 | ||
![]() |
7d58238e06 | ||
![]() |
a883faae9d | ||
![]() |
a7040af346 | ||
![]() |
5cca3569e9 | ||
![]() |
f2857b3243 | ||
![]() |
8e84210852 | ||
![]() |
8c2df79536 | ||
![]() |
ebe33cff18 | ||
![]() |
a551d7ebd9 | ||
![]() |
79a29826a4 | ||
![]() |
3564e96a00 | ||
![]() |
f6f8f27668 | ||
![]() |
dff54464c6 | ||
![]() |
e987cdaaae | ||
![]() |
6cd0e58fd0 | ||
![]() |
f794c090a5 | ||
![]() |
0d3197e437 | ||
![]() |
150bf7fc65 | ||
![]() |
51004b14d9 | ||
![]() |
ea7e15b447 | ||
![]() |
8e637a2ec7 | ||
![]() |
96d886380a | ||
![]() |
981c69040f | ||
![]() |
de90c276af | ||
![]() |
0129a8579f | ||
![]() |
11ed4a56bd | ||
![]() |
d75a0e69a0 | ||
![]() |
1faad73381 | ||
![]() |
d2499cd69d | ||
![]() |
98df77439c | ||
![]() |
81bbbe4eec | ||
![]() |
9f530525d7 | ||
![]() |
129283066f | ||
![]() |
0dc6a726c1 | ||
![]() |
4636499439 | ||
![]() |
9a16eb2895 | ||
![]() |
270a080b55 | ||
![]() |
1cf9a55e3e | ||
![]() |
6bcd91a801 | ||
![]() |
7ed25ddc74 | ||
![]() |
ae557c30d3 | ||
![]() |
5a1800d642 | ||
![]() |
d8fe7a52d6 | ||
![]() |
791ecfbb32 | ||
![]() |
5ff4473083 | ||
![]() |
d1e88a30cb | ||
![]() |
7eae7756f5 | ||
![]() |
4e3cd01aad | ||
![]() |
dbaee284e4 | ||
![]() |
8253bfe2e0 | ||
![]() |
828b5ad8bb | ||
![]() |
fedad26c13 | ||
![]() |
a526bb70ea | ||
![]() |
5f6de610e1 | ||
![]() |
02397868fc | ||
![]() |
265a6b9b68 | ||
![]() |
10e7c533d7 | ||
![]() |
0ed3c5a5ec | ||
![]() |
c2b06a02bf | ||
![]() |
e0458a8fde | ||
![]() |
21fb5f75b8 | ||
![]() |
fb99412193 |
51
.github/workflows/android-branch-auto-sync.yml
vendored
Normal file
51
.github/workflows/android-branch-auto-sync.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
name: Android Branch Auto Sync
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "README.md"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
branches:
|
||||
- Alpha
|
||||
- android-open
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- Alpha
|
||||
- android-open
|
||||
|
||||
jobs:
|
||||
update-dependencies:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.name 'GitHub Action'
|
||||
git config --global user.email 'action@github.com'
|
||||
|
||||
- name: Sync android-real with Alpha rebase android-open
|
||||
run: |
|
||||
git fetch origin
|
||||
git checkout origin/Alpha -b android-real
|
||||
git merge --squash origin/android-open
|
||||
git commit -m "Android: patch"
|
||||
|
||||
- name: Check for conflicts
|
||||
run: |
|
||||
CONFLICTS=$(git diff --name-only --diff-filter=U)
|
||||
if [ ! -z "$CONFLICTS" ]; then
|
||||
echo "There are conflicts in the following files:"
|
||||
echo $CONFLICTS
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Push changes
|
||||
run: |
|
||||
git push origin android-real --force
|
28
.github/workflows/cmfa-update-deps-trigger.yml
vendored
Normal file
28
.github/workflows/cmfa-update-deps-trigger.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
name: CMFA auto update-dependencies trigger
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- Alpha
|
||||
|
||||
jobs:
|
||||
update-dependencies:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tibdex/github-app-token@v1
|
||||
id: generate-token
|
||||
with:
|
||||
app_id: ${{ secrets.MAINTAINER_APPID }}
|
||||
private_key: ${{ secrets.MAINTAINER_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Trigger update-dependencies
|
||||
run: |
|
||||
curl -X POST https://api.github.com/repos/MetaCubeX/ClashMetaForAndroid/dispatches \
|
||||
-H "Accept: application/vnd.github.everest-preview+json" \
|
||||
-H "Authorization: token ${{ steps.generate-token.outputs.token }}" \
|
||||
-d '{"event_type": "core-updated"}'
|
||||
# Send "core-updated" to MetaCubeX/ClashMetaForAndroid to trigger update-dependencies
|
||||
|
302
README.md
302
README.md
@ -21,7 +21,7 @@
|
||||
## Features
|
||||
|
||||
- Local HTTP/HTTPS/SOCKS server with authentication support
|
||||
- VMess, Shadowsocks, Trojan, Snell protocol support for remote connections
|
||||
- VMess, VLESS, Shadowsocks, Trojan, Snell, TUIC, Hysteria protocol support
|
||||
- Built-in DNS server that aims to minimize DNS pollution attack impact, supports DoH/DoT upstream and fake IP.
|
||||
- Rules based off domains, GEOIP, IPCIDR or Process to forward packets to different nodes
|
||||
- Remote groups allow users to implement powerful rules. Supports automatic fallback, load balancing or auto select node
|
||||
@ -32,259 +32,41 @@
|
||||
|
||||
## Dashboard
|
||||
|
||||
We made an official web dashboard providing first class support for this project, check it out
|
||||
at [metacubexd](https://github.com/MetaCubeX/metacubexd)
|
||||
A web dashboard with first-class support for this project has been created; it can be checked out at [metacubexd](https://github.com/MetaCubeX/metacubexd).
|
||||
|
||||
## Wiki
|
||||
## Configration example
|
||||
|
||||
Configuration examples can be found
|
||||
at [/docs/config.yaml](https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml), while documentation can be
|
||||
found [Clash.Meta Wiki](https://clash-meta.wiki).
|
||||
Configuration example is located at [/docs/config.yaml](https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml).
|
||||
|
||||
## Build
|
||||
## Docs
|
||||
|
||||
You should install [golang](https://go.dev) first.
|
||||
Documentation can be found in [Clash.Meta Docs](https://clash-meta.wiki).
|
||||
|
||||
Then get the source code of Clash.Meta:
|
||||
## For development
|
||||
|
||||
Requirements:
|
||||
[Go 1.20 or newer](https://go.dev/dl/)
|
||||
|
||||
Build Clash.Meta:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/MetaCubeX/Clash.Meta.git
|
||||
cd Clash.Meta && go mod download
|
||||
go build
|
||||
```
|
||||
|
||||
If you can't visit GitHub, you should set proxy first:
|
||||
Set go proxy if a connection to GitHub is not possible:
|
||||
|
||||
```shell
|
||||
go env -w GOPROXY=https://goproxy.io,direct
|
||||
```
|
||||
|
||||
Now you can build it:
|
||||
|
||||
```shell
|
||||
go build
|
||||
```
|
||||
|
||||
If you need gvisor for tun stack, build with:
|
||||
Build with gvisor tun stack:
|
||||
|
||||
```shell
|
||||
go build -tags with_gvisor
|
||||
```
|
||||
|
||||
<!-- ## Advanced usage of this fork -->
|
||||
|
||||
<!-- ### DNS configuration
|
||||
|
||||
Support `geosite` with `fallback-filter`.
|
||||
|
||||
Restore `Redir remote resolution`.
|
||||
|
||||
Support resolve ip with a `Proxy Tunnel`.
|
||||
|
||||
```yaml
|
||||
proxy-groups:
|
||||
- name: DNS
|
||||
type: url-test
|
||||
use:
|
||||
- HK
|
||||
url: http://cp.cloudflare.com
|
||||
interval: 180
|
||||
lazy: true
|
||||
```
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
enable: true
|
||||
use-hosts: true
|
||||
ipv6: false
|
||||
enhanced-mode: redir-host
|
||||
fake-ip-range: 198.18.0.1/16
|
||||
listen: 127.0.0.1:6868
|
||||
default-nameserver:
|
||||
- 119.29.29.29
|
||||
- 114.114.114.114
|
||||
nameserver:
|
||||
- https://doh.pub/dns-query
|
||||
- tls://223.5.5.5:853
|
||||
fallback:
|
||||
- "https://1.0.0.1/dns-query#DNS" # append the proxy adapter name or group name to the end of DNS URL with '#' prefix.
|
||||
- "tls://8.8.4.4:853#DNS"
|
||||
fallback-filter:
|
||||
geoip: false
|
||||
geosite:
|
||||
- gfw # `geosite` filter only use fallback server to resolve ip, prevent DNS leaks to unsafe DNS providers.
|
||||
domain:
|
||||
- +.example.com
|
||||
ipcidr:
|
||||
- 0.0.0.0/32
|
||||
```
|
||||
|
||||
### TUN configuration
|
||||
|
||||
Supports macOS, Linux and Windows.
|
||||
|
||||
Built-in [Wintun](https://www.wintun.net) driver.
|
||||
|
||||
```yaml
|
||||
# Enable the TUN listener
|
||||
tun:
|
||||
enable: true
|
||||
stack: system # system/gvisor
|
||||
dns-hijack:
|
||||
- 0.0.0.0:53 # additional dns server listen on TUN
|
||||
auto-route: true # auto set global route
|
||||
```
|
||||
|
||||
### Rules configuration
|
||||
|
||||
- Support rule `GEOSITE`.
|
||||
- Support rule-providers `RULE-SET`.
|
||||
- Support `multiport` condition for rule `SRC-PORT` and `DST-PORT`.
|
||||
- Support `network` condition for all rules.
|
||||
- Support source IPCIDR condition for all rules, just append to the end.
|
||||
- The `GEOSITE` databases via https://github.com/Loyalsoldier/v2ray-rules-dat.
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
# network(tcp/udp) condition for all rules
|
||||
- DOMAIN-SUFFIX,bilibili.com,DIRECT,tcp
|
||||
- DOMAIN-SUFFIX,bilibili.com,REJECT,udp
|
||||
|
||||
# multiport condition for rules SRC-PORT and DST-PORT
|
||||
- DST-PORT,123/136/137-139,DIRECT,udp
|
||||
|
||||
# rule GEOSITE
|
||||
- GEOSITE,category-ads-all,REJECT
|
||||
- GEOSITE,icloud@cn,DIRECT
|
||||
- GEOSITE,apple@cn,DIRECT
|
||||
- GEOSITE,apple-cn,DIRECT
|
||||
- GEOSITE,microsoft@cn,DIRECT
|
||||
- GEOSITE,facebook,PROXY
|
||||
- GEOSITE,youtube,PROXY
|
||||
- GEOSITE,geolocation-cn,DIRECT
|
||||
- GEOSITE,geolocation-!cn,PROXY
|
||||
|
||||
# source IPCIDR condition for all rules in gateway proxy
|
||||
#- GEOSITE,geolocation-!cn,REJECT,192.168.1.88/32,192.168.1.99/32
|
||||
|
||||
- GEOIP,telegram,PROXY,no-resolve
|
||||
- GEOIP,private,DIRECT,no-resolve
|
||||
- GEOIP,cn,DIRECT
|
||||
|
||||
- MATCH,PROXY
|
||||
```
|
||||
|
||||
### Proxies configuration
|
||||
|
||||
Active health detection `urltest / fallback` (based on tcp handshake, multiple failures within a limited time will actively trigger health detection to use the node)
|
||||
|
||||
Support `Policy Group Filter`
|
||||
|
||||
```yaml
|
||||
proxy-groups:
|
||||
- name: 🚀 HK Group
|
||||
type: select
|
||||
use:
|
||||
- ALL
|
||||
filter: "HK"
|
||||
|
||||
- name: 🚀 US Group
|
||||
type: select
|
||||
use:
|
||||
- ALL
|
||||
filter: "US"
|
||||
|
||||
proxy-providers:
|
||||
ALL:
|
||||
type: http
|
||||
url: "xxxxx"
|
||||
interval: 3600
|
||||
path: "xxxxx"
|
||||
health-check:
|
||||
enable: true
|
||||
interval: 600
|
||||
url: http://www.gstatic.com/generate_204
|
||||
```
|
||||
|
||||
Support outbound transport protocol `VLESS`.
|
||||
|
||||
The XTLS support (TCP/UDP) transport by the XRAY-CORE.
|
||||
|
||||
```yaml
|
||||
proxies:
|
||||
- name: "vless"
|
||||
type: vless
|
||||
server: server
|
||||
port: 443
|
||||
uuid: uuid
|
||||
servername: example.com # AKA SNI
|
||||
# flow: xtls-rprx-direct # xtls-rprx-origin # enable XTLS
|
||||
# skip-cert-verify: true
|
||||
|
||||
- name: "vless-ws"
|
||||
type: vless
|
||||
server: server
|
||||
port: 443
|
||||
uuid: uuid
|
||||
tls: true
|
||||
udp: true
|
||||
network: ws
|
||||
servername: example.com # priority over wss host
|
||||
# skip-cert-verify: true
|
||||
ws-opts:
|
||||
path: /path
|
||||
headers: { Host: example.com, Edge: "12a00c4.fm.huawei.com:82897" }
|
||||
|
||||
- name: "vless-grpc"
|
||||
type: vless
|
||||
server: server
|
||||
port: 443
|
||||
uuid: uuid
|
||||
tls: true
|
||||
udp: true
|
||||
network: grpc
|
||||
servername: example.com # priority over wss host
|
||||
# skip-cert-verify: true
|
||||
grpc-opts:
|
||||
grpc-service-name: grpcname
|
||||
```
|
||||
|
||||
Support outbound transport protocol `Wireguard`
|
||||
|
||||
```yaml
|
||||
proxies:
|
||||
- name: "wg"
|
||||
type: wireguard
|
||||
server: 162.159.192.1
|
||||
port: 2480
|
||||
ip: 172.16.0.2
|
||||
ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5
|
||||
private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU=
|
||||
public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=
|
||||
udp: true
|
||||
```
|
||||
|
||||
Support outbound transport protocol `Tuic`
|
||||
|
||||
```yaml
|
||||
proxies:
|
||||
- name: "tuic"
|
||||
server: www.example.com
|
||||
port: 10443
|
||||
type: tuic
|
||||
token: TOKEN
|
||||
# ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server'
|
||||
# heartbeat-interval: 10000
|
||||
# alpn: [h3]
|
||||
# disable-sni: true
|
||||
reduce-rtt: true
|
||||
# request-timeout: 8000
|
||||
udp-relay-mode: native # Available: "native", "quic". Default: "native"
|
||||
# congestion-controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic"
|
||||
# max-udp-relay-packet-size: 1500
|
||||
# fast-open: true
|
||||
# skip-cert-verify: true
|
||||
``` -->
|
||||
|
||||
### IPTABLES configuration
|
||||
|
||||
Work on Linux OS which supported `iptables`
|
||||
@ -298,61 +80,9 @@ iptables:
|
||||
inbound-interface: eth0 # detect the inbound interface, default is 'lo'
|
||||
```
|
||||
|
||||
### General installation guide for Linux
|
||||
|
||||
- Create user given name `clash-meta`
|
||||
|
||||
- Download and decompress pre-built binaries from [releases](https://github.com/MetaCubeX/Clash.Meta/releases)
|
||||
|
||||
- Rename executable file to `Clash-Meta` and move to `/usr/local/bin/`
|
||||
|
||||
- Create folder `/etc/Clash-Meta/` as working directory
|
||||
|
||||
Run Meta Kernel by user `clash-meta` as a daemon.
|
||||
|
||||
Create the systemd configuration file at `/etc/systemd/system/Clash-Meta.service`:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=Clash-Meta Daemon, Another Clash Kernel.
|
||||
After=network.target NetworkManager.service systemd-networkd.service iwd.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=clash-meta
|
||||
Group=clash-meta
|
||||
LimitNPROC=500
|
||||
LimitNOFILE=1000000
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE
|
||||
Restart=always
|
||||
ExecStartPre=/usr/bin/sleep 1s
|
||||
ExecStart=/usr/local/bin/Clash-Meta -d /etc/Clash-Meta
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Launch clash-meta daemon on system startup with:
|
||||
|
||||
```shell
|
||||
$ systemctl enable Clash-Meta
|
||||
```
|
||||
|
||||
Launch clash-meta daemon immediately with:
|
||||
|
||||
```shell
|
||||
$ systemctl start Clash-Meta
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
If you want to build an application that uses clash as a library, check out
|
||||
the [GitHub Wiki](https://github.com/Dreamacro/clash/wiki/use-clash-as-a-library)
|
||||
|
||||
## Debugging
|
||||
|
||||
Check [wiki](https://github.com/MetaCubeX/Clash.Meta/wiki/How-to-use-debug-api) to get an instruction on using debug
|
||||
Check [wiki](https://wiki.metacubex.one/api/#debug) to get an instruction on using debug
|
||||
API.
|
||||
|
||||
## Credits
|
||||
|
@ -30,13 +30,13 @@ const (
|
||||
|
||||
type extraProxyState struct {
|
||||
history *queue.Queue[C.DelayHistory]
|
||||
alive *atomic.Bool
|
||||
alive atomic.Bool
|
||||
}
|
||||
|
||||
type Proxy struct {
|
||||
C.ProxyAdapter
|
||||
history *queue.Queue[C.DelayHistory]
|
||||
alive *atomic.Bool
|
||||
alive atomic.Bool
|
||||
url string
|
||||
extra *xsync.MapOf[string, *extraProxyState]
|
||||
}
|
||||
|
@ -1,13 +1,17 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type Addition func(metadata *C.Metadata)
|
||||
|
||||
func (a Addition) Apply(metadata *C.Metadata) {
|
||||
a(metadata)
|
||||
func ApplyAdditions(metadata *C.Metadata, additions ...Addition) {
|
||||
for _, addition := range additions {
|
||||
addition(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
func WithInName(name string) Addition {
|
||||
@ -33,3 +37,29 @@ func WithSpecialProxy(specialProxy string) Addition {
|
||||
metadata.SpecialProxy = specialProxy
|
||||
}
|
||||
}
|
||||
|
||||
func WithDstAddr(addr net.Addr) Addition {
|
||||
return func(metadata *C.Metadata) {
|
||||
_ = metadata.SetRemoteAddr(addr)
|
||||
}
|
||||
}
|
||||
|
||||
func WithSrcAddr(addr net.Addr) Addition {
|
||||
return func(metadata *C.Metadata) {
|
||||
m := C.Metadata{}
|
||||
if err := m.SetRemoteAddr(addr);err ==nil{
|
||||
metadata.SrcIP = m.DstIP
|
||||
metadata.SrcPort = m.DstPort
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithInAddr(addr net.Addr) Addition {
|
||||
return func(metadata *C.Metadata) {
|
||||
m := C.Metadata{}
|
||||
if err := m.SetRemoteAddr(addr);err ==nil{
|
||||
metadata.InIP = m.DstIP
|
||||
metadata.InPort = m.DstPort
|
||||
}
|
||||
}
|
||||
}
|
||||
|
45
adapter/inbound/auth.go
Normal file
45
adapter/inbound/auth.go
Normal file
@ -0,0 +1,45 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
var skipAuthPrefixes []netip.Prefix
|
||||
|
||||
func SetSkipAuthPrefixes(prefixes []netip.Prefix) {
|
||||
skipAuthPrefixes = prefixes
|
||||
}
|
||||
|
||||
func SkipAuthPrefixes() []netip.Prefix {
|
||||
return skipAuthPrefixes
|
||||
}
|
||||
|
||||
func SkipAuthRemoteAddr(addr net.Addr) bool {
|
||||
m := C.Metadata{}
|
||||
if err := m.SetRemoteAddr(addr); err != nil {
|
||||
return false
|
||||
}
|
||||
return skipAuth(m.AddrPort().Addr())
|
||||
}
|
||||
|
||||
func SkipAuthRemoteAddress(addr string) bool {
|
||||
m := C.Metadata{}
|
||||
if err := m.SetRemoteAddress(addr); err != nil {
|
||||
return false
|
||||
}
|
||||
return skipAuth(m.AddrPort().Addr())
|
||||
}
|
||||
|
||||
func skipAuth(addr netip.Addr) bool {
|
||||
if addr.IsValid() {
|
||||
for _, prefix := range skipAuthPrefixes {
|
||||
if prefix.Contains(addr.Unmap()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
@ -4,25 +4,17 @@ import (
|
||||
"net"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/context"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
// NewHTTP receive normal http request and return HTTPContext
|
||||
func NewHTTP(target socks5.Addr, source net.Addr, conn net.Conn, additions ...Addition) *context.ConnContext {
|
||||
func NewHTTP(target socks5.Addr, srcConn net.Conn, conn net.Conn, additions ...Addition) (net.Conn, *C.Metadata) {
|
||||
metadata := parseSocksAddr(target)
|
||||
metadata.NetWork = C.TCP
|
||||
metadata.Type = C.HTTP
|
||||
for _, addition := range additions {
|
||||
addition.Apply(metadata)
|
||||
}
|
||||
if ip, port, err := parseAddr(source); err == nil {
|
||||
metadata.SrcIP = ip
|
||||
metadata.SrcPort = port
|
||||
}
|
||||
if ip, port, err := parseAddr(conn.LocalAddr()); err == nil {
|
||||
metadata.InIP = ip
|
||||
metadata.InPort = port
|
||||
}
|
||||
return context.NewConnContext(conn, metadata)
|
||||
metadata.RawSrcAddr = srcConn.RemoteAddr()
|
||||
metadata.RawDstAddr = srcConn.LocalAddr()
|
||||
ApplyAdditions(metadata, WithSrcAddr(srcConn.RemoteAddr()), WithInAddr(conn.LocalAddr()))
|
||||
ApplyAdditions(metadata, additions...)
|
||||
return conn, metadata
|
||||
}
|
||||
|
@ -5,23 +5,13 @@ import (
|
||||
"net/http"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/context"
|
||||
)
|
||||
|
||||
// NewHTTPS receive CONNECT request and return ConnContext
|
||||
func NewHTTPS(request *http.Request, conn net.Conn, additions ...Addition) *context.ConnContext {
|
||||
func NewHTTPS(request *http.Request, conn net.Conn, additions ...Addition) (net.Conn, *C.Metadata) {
|
||||
metadata := parseHTTPAddr(request)
|
||||
metadata.Type = C.HTTPS
|
||||
for _, addition := range additions {
|
||||
addition.Apply(metadata)
|
||||
}
|
||||
if ip, port, err := parseAddr(conn.RemoteAddr()); err == nil {
|
||||
metadata.SrcIP = ip
|
||||
metadata.SrcPort = port
|
||||
}
|
||||
if ip, port, err := parseAddr(conn.LocalAddr()); err == nil {
|
||||
metadata.InIP = ip
|
||||
metadata.InPort = port
|
||||
}
|
||||
return context.NewConnContext(conn, metadata)
|
||||
ApplyAdditions(metadata, WithSrcAddr(conn.RemoteAddr()), WithInAddr(conn.LocalAddr()))
|
||||
ApplyAdditions(metadata, additions...)
|
||||
return conn, metadata
|
||||
}
|
||||
|
@ -5,38 +5,18 @@ import (
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
// PacketAdapter is a UDP Packet adapter for socks/redir/tun
|
||||
type PacketAdapter struct {
|
||||
C.UDPPacket
|
||||
metadata *C.Metadata
|
||||
}
|
||||
|
||||
// Metadata returns destination metadata
|
||||
func (s *PacketAdapter) Metadata() *C.Metadata {
|
||||
return s.metadata
|
||||
}
|
||||
|
||||
// NewPacket is PacketAdapter generator
|
||||
func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type, additions ...Addition) C.PacketAdapter {
|
||||
func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type, additions ...Addition) (C.UDPPacket, *C.Metadata) {
|
||||
metadata := parseSocksAddr(target)
|
||||
metadata.NetWork = C.UDP
|
||||
metadata.Type = source
|
||||
for _, addition := range additions {
|
||||
addition.Apply(metadata)
|
||||
}
|
||||
if ip, port, err := parseAddr(packet.LocalAddr()); err == nil {
|
||||
metadata.SrcIP = ip
|
||||
metadata.SrcPort = port
|
||||
}
|
||||
metadata.RawSrcAddr = packet.LocalAddr()
|
||||
metadata.RawDstAddr = metadata.UDPAddr()
|
||||
ApplyAdditions(metadata, WithSrcAddr(packet.LocalAddr()))
|
||||
if p, ok := packet.(C.UDPPacketInAddr); ok {
|
||||
if ip, port, err := parseAddr(p.InAddr()); err == nil {
|
||||
metadata.InIP = ip
|
||||
metadata.InPort = port
|
||||
}
|
||||
ApplyAdditions(metadata, WithInAddr(p.InAddr()))
|
||||
}
|
||||
ApplyAdditions(metadata, additions...)
|
||||
|
||||
return &PacketAdapter{
|
||||
packet,
|
||||
metadata,
|
||||
}
|
||||
return packet, metadata
|
||||
}
|
||||
|
@ -2,51 +2,17 @@ package inbound
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/context"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
// NewSocket receive TCP inbound and return ConnContext
|
||||
func NewSocket(target socks5.Addr, conn net.Conn, source C.Type, additions ...Addition) *context.ConnContext {
|
||||
func NewSocket(target socks5.Addr, conn net.Conn, source C.Type, additions ...Addition) (net.Conn, *C.Metadata) {
|
||||
metadata := parseSocksAddr(target)
|
||||
metadata.NetWork = C.TCP
|
||||
metadata.Type = source
|
||||
for _, addition := range additions {
|
||||
addition.Apply(metadata)
|
||||
}
|
||||
|
||||
if ip, port, err := parseAddr(conn.RemoteAddr()); err == nil {
|
||||
metadata.SrcIP = ip
|
||||
metadata.SrcPort = port
|
||||
}
|
||||
if ip, port, err := parseAddr(conn.LocalAddr()); err == nil {
|
||||
metadata.InIP = ip
|
||||
metadata.InPort = port
|
||||
}
|
||||
|
||||
return context.NewConnContext(conn, metadata)
|
||||
}
|
||||
|
||||
func NewInner(conn net.Conn, address string) *context.ConnContext {
|
||||
metadata := &C.Metadata{}
|
||||
metadata.NetWork = C.TCP
|
||||
metadata.Type = C.INNER
|
||||
metadata.DNSMode = C.DNSNormal
|
||||
metadata.Process = C.ClashName
|
||||
if h, port, err := net.SplitHostPort(address); err == nil {
|
||||
if port, err := strconv.ParseUint(port, 10, 16); err == nil {
|
||||
metadata.DstPort = uint16(port)
|
||||
}
|
||||
if ip, err := netip.ParseAddr(h); err == nil {
|
||||
metadata.DstIP = ip
|
||||
} else {
|
||||
metadata.Host = h
|
||||
}
|
||||
}
|
||||
|
||||
return context.NewConnContext(conn, metadata)
|
||||
ApplyAdditions(metadata, WithSrcAddr(conn.RemoteAddr()), WithInAddr(conn.LocalAddr()))
|
||||
ApplyAdditions(metadata, additions...)
|
||||
return conn, metadata
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
@ -62,29 +61,3 @@ func parseHTTPAddr(request *http.Request) *C.Metadata {
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
func parseAddr(addr net.Addr) (netip.Addr, uint16, error) {
|
||||
// Filter when net.Addr interface is nil
|
||||
if addr == nil {
|
||||
return netip.Addr{}, 0, errors.New("nil addr")
|
||||
}
|
||||
if rawAddr, ok := addr.(interface{ RawAddr() net.Addr }); ok {
|
||||
ip, port, err := parseAddr(rawAddr.RawAddr())
|
||||
if err == nil {
|
||||
return ip, port, err
|
||||
}
|
||||
}
|
||||
addrStr := addr.String()
|
||||
host, port, err := net.SplitHostPort(addrStr)
|
||||
if err != nil {
|
||||
return netip.Addr{}, 0, err
|
||||
}
|
||||
|
||||
var uint16Port uint16
|
||||
if port, err := strconv.ParseUint(port, 10, 16); err == nil {
|
||||
uint16Port = uint16(port)
|
||||
}
|
||||
|
||||
ip, err := netip.ParseAddr(host)
|
||||
return ip, uint16Port, err
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ type Hysteria struct {
|
||||
}
|
||||
|
||||
func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||
tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), h.genHdc(ctx, opts...))
|
||||
tcpConn, err := h.client.DialTCP(metadata.String(), metadata.DstPort, h.genHdc(ctx, opts...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ type Hysteria2Option struct {
|
||||
func (h *Hysteria2) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
|
||||
options := h.Base.DialOptions(opts...)
|
||||
h.dialer.SetDialer(dialer.NewDialer(options...))
|
||||
c, err := h.client.DialConn(ctx, M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||
c, err := h.client.DialConn(ctx, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -15,6 +15,10 @@ type Reject struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
type RejectOption struct {
|
||||
Name string `proxy:"name"`
|
||||
}
|
||||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
func (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||
return NewConn(nopConn{}, r), nil
|
||||
@ -25,6 +29,16 @@ func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
|
||||
return newPacketConn(nopPacketConn{}, r), nil
|
||||
}
|
||||
|
||||
func NewRejectWithOption(option RejectOption) *Reject {
|
||||
return &Reject{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
tp: C.Direct,
|
||||
udp: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewReject() *Reject {
|
||||
return &Reject{
|
||||
Base: &Base{
|
||||
|
@ -123,9 +123,9 @@ func (ss *ShadowSocks) StreamConnContext(ctx context.Context, c net.Conn, metada
|
||||
}
|
||||
}
|
||||
if useEarly {
|
||||
return ss.method.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil
|
||||
return ss.method.DialEarlyConn(c, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort)), nil
|
||||
} else {
|
||||
return ss.method.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||
return ss.method.DialConn(c, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ type ProxyBase interface {
|
||||
func (s *SingMux) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
|
||||
options := s.base.DialOptions(opts...)
|
||||
s.dialer.SetDialer(dialer.NewDialer(options...))
|
||||
c, err := s.client.DialContext(ctx, "tcp", M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||
c, err := s.client.DialContext(ctx, "tcp", M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
|
||||
N "github.com/Dreamacro/clash/common/net"
|
||||
@ -136,7 +137,8 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
|
||||
}
|
||||
}
|
||||
|
||||
bindAddr, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdUDPAssociate, user)
|
||||
udpAssocateAddr := socks5.AddrFromStdAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), 0))
|
||||
bindAddr, err := socks5.ClientHandshake(c, udpAssocateAddr, socks5.CmdUDPAssociate, user)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("client hanshake error: %w", err)
|
||||
return
|
||||
|
@ -260,10 +260,10 @@ func (v *Vmess) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err
|
||||
} else {
|
||||
if N.NeedHandshake(c) {
|
||||
conn = v.client.DialEarlyConn(c,
|
||||
M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||
M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
|
||||
} else {
|
||||
conn, err = v.client.DialConn(c,
|
||||
M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||
M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@ -284,7 +284,7 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d
|
||||
safeConnClose(c, err)
|
||||
}(c)
|
||||
|
||||
c, err = v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress()))
|
||||
c, err = v.client.DialConn(c, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ type GroupBase struct {
|
||||
failedTestMux sync.Mutex
|
||||
failedTimes int
|
||||
failedTime time.Time
|
||||
failedTesting *atomic.Bool
|
||||
failedTesting atomic.Bool
|
||||
proxies [][]C.Proxy
|
||||
versions []atomic.Uint32
|
||||
}
|
||||
|
62
adapter/outboundgroup/patch.go
Normal file
62
adapter/outboundgroup/patch.go
Normal file
@ -0,0 +1,62 @@
|
||||
package outboundgroup
|
||||
|
||||
import (
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/constant/provider"
|
||||
)
|
||||
|
||||
type ProxyGroup interface {
|
||||
C.ProxyAdapter
|
||||
|
||||
Providers() []provider.ProxyProvider
|
||||
Proxies() []C.Proxy
|
||||
Now() string
|
||||
}
|
||||
|
||||
func (f *Fallback) Providers() []provider.ProxyProvider {
|
||||
return f.providers
|
||||
}
|
||||
|
||||
func (lb *LoadBalance) Providers() []provider.ProxyProvider {
|
||||
return lb.providers
|
||||
}
|
||||
|
||||
func (f *Fallback) Proxies() []C.Proxy {
|
||||
return f.GetProxies(false)
|
||||
}
|
||||
|
||||
func (lb *LoadBalance) Proxies() []C.Proxy {
|
||||
return lb.GetProxies(false)
|
||||
}
|
||||
|
||||
func (lb *LoadBalance) Now() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Relay) Providers() []provider.ProxyProvider {
|
||||
return r.providers
|
||||
}
|
||||
|
||||
func (r *Relay) Proxies() []C.Proxy {
|
||||
return r.GetProxies(false)
|
||||
}
|
||||
|
||||
func (r *Relay) Now() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Selector) Providers() []provider.ProxyProvider {
|
||||
return s.providers
|
||||
}
|
||||
|
||||
func (s *Selector) Proxies() []C.Proxy {
|
||||
return s.GetProxies(false)
|
||||
}
|
||||
|
||||
func (u *URLTest) Providers() []provider.ProxyProvider {
|
||||
return u.providers
|
||||
}
|
||||
|
||||
func (u *URLTest) Proxies() []C.Proxy {
|
||||
return u.GetProxies(false)
|
||||
}
|
@ -120,6 +120,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
|
||||
break
|
||||
}
|
||||
proxy = outbound.NewDirectWithOption(*directOption)
|
||||
case "reject":
|
||||
rejectOption := &outbound.RejectOption{}
|
||||
err = decoder.Decode(mapping, rejectOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy = outbound.NewRejectWithOption(*rejectOption)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
|
||||
const (
|
||||
defaultURLTestTimeout = time.Second * 5
|
||||
defaultURLTestURL = "https://www.gstatic.com/generate_204"
|
||||
)
|
||||
|
||||
type HealthCheckOption struct {
|
||||
@ -34,12 +35,12 @@ type HealthCheck struct {
|
||||
url string
|
||||
extra map[string]*extraOption
|
||||
mu sync.Mutex
|
||||
started *atomic.Bool
|
||||
started atomic.Bool
|
||||
proxies []C.Proxy
|
||||
interval uint
|
||||
interval time.Duration
|
||||
lazy bool
|
||||
expectedStatus utils.IntRanges[uint16]
|
||||
lastTouch *atomic.Int64
|
||||
lastTouch atomic.TypedValue[time.Time]
|
||||
done chan struct{}
|
||||
singleDo *singledo.Single[struct{}]
|
||||
}
|
||||
@ -50,13 +51,14 @@ func (hc *HealthCheck) process() {
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
|
||||
ticker := time.NewTicker(hc.interval)
|
||||
hc.start()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
now := time.Now().Unix()
|
||||
if !hc.lazy || now-hc.lastTouch.Load() < int64(hc.interval) {
|
||||
lastTouch := hc.lastTouch.Load()
|
||||
since := time.Since(lastTouch)
|
||||
if !hc.lazy || since < hc.interval {
|
||||
hc.check()
|
||||
} else {
|
||||
log.Debugln("Skip once health check because we are lazy")
|
||||
@ -85,7 +87,7 @@ func (hc *HealthCheck) registerHealthCheckTask(url string, expectedStatus utils.
|
||||
|
||||
// if the provider has not set up health checks, then modify it to be the same as the group's interval
|
||||
if hc.interval == 0 {
|
||||
hc.interval = interval
|
||||
hc.interval = time.Duration(interval) * time.Second
|
||||
}
|
||||
|
||||
if hc.extra == nil {
|
||||
@ -135,7 +137,7 @@ func (hc *HealthCheck) auto() bool {
|
||||
}
|
||||
|
||||
func (hc *HealthCheck) touch() {
|
||||
hc.lastTouch.Store(time.Now().Unix())
|
||||
hc.lastTouch.Store(time.Now())
|
||||
}
|
||||
|
||||
func (hc *HealthCheck) start() {
|
||||
@ -147,6 +149,11 @@ func (hc *HealthCheck) stop() {
|
||||
}
|
||||
|
||||
func (hc *HealthCheck) check() {
|
||||
|
||||
if len(hc.proxies) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
_, _, _ = hc.singleDo.Do(func() (struct{}, error) {
|
||||
id := utils.NewUUIDV4().String()
|
||||
log.Debugln("Start New Health Checking {%s}", id)
|
||||
@ -222,17 +229,16 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool, exp
|
||||
if len(url) == 0 {
|
||||
interval = 0
|
||||
expectedStatus = nil
|
||||
url = defaultURLTestURL
|
||||
}
|
||||
|
||||
return &HealthCheck{
|
||||
proxies: proxies,
|
||||
url: url,
|
||||
extra: map[string]*extraOption{},
|
||||
started: atomic.NewBool(false),
|
||||
interval: interval,
|
||||
interval: time.Duration(interval) * time.Second,
|
||||
lazy: lazy,
|
||||
expectedStatus: expectedStatus,
|
||||
lastTouch: atomic.NewInt64(0),
|
||||
done: make(chan struct{}, 1),
|
||||
singleDo: singledo.NewSingle[struct{}](time.Second),
|
||||
}
|
||||
|
@ -68,9 +68,6 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
|
||||
case "http":
|
||||
if schema.Path != "" {
|
||||
path := C.Path.Resolve(schema.Path)
|
||||
if !C.Path.IsSafePath(path) {
|
||||
return nil, fmt.Errorf("%w: %s", errSubPath, path)
|
||||
}
|
||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
||||
} else {
|
||||
path := C.Path.GetPathByHash("proxies", schema.URL)
|
||||
|
34
adapter/provider/patch.go
Normal file
34
adapter/provider/patch.go
Normal file
@ -0,0 +1,34 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
suspended bool
|
||||
)
|
||||
|
||||
type UpdatableProvider interface {
|
||||
UpdatedAt() time.Time
|
||||
}
|
||||
|
||||
func (pp *proxySetProvider) UpdatedAt() time.Time {
|
||||
return pp.Fetcher.UpdatedAt
|
||||
}
|
||||
|
||||
func (pp *proxySetProvider) Close() error {
|
||||
pp.healthCheck.close()
|
||||
pp.Fetcher.Destroy()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cp *compatibleProvider) Close() error {
|
||||
cp.healthCheck.close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Suspend(s bool) {
|
||||
suspended = s
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/dlclark/regexp2"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@ -13,45 +12,24 @@ type SubscriptionInfo struct {
|
||||
Expire int64
|
||||
}
|
||||
|
||||
func NewSubscriptionInfo(str string) (si *SubscriptionInfo, err error) {
|
||||
si = &SubscriptionInfo{}
|
||||
str = strings.ToLower(str)
|
||||
reTraffic := regexp2.MustCompile("upload=(\\d+); download=(\\d+); total=(\\d+)", 0)
|
||||
reExpire := regexp2.MustCompile("expire=(\\d+)", 0)
|
||||
|
||||
match, err := reTraffic.FindStringMatch(str)
|
||||
if err != nil || match == nil {
|
||||
return nil, err
|
||||
}
|
||||
group := match.Groups()
|
||||
si.Upload, err = str2uint64(group[1].String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
si.Download, err = str2uint64(group[2].String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
si.Total, err = str2uint64(group[3].String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
match, _ = reExpire.FindStringMatch(str)
|
||||
if match != nil {
|
||||
group = match.Groups()
|
||||
si.Expire, err = str2uint64(group[1].String())
|
||||
func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo, err error) {
|
||||
userinfo = strings.ToLower(userinfo)
|
||||
userinfo = strings.ReplaceAll(userinfo, " ", "")
|
||||
si = new(SubscriptionInfo)
|
||||
for _, field := range strings.Split(userinfo, ";") {
|
||||
switch name, value, _ := strings.Cut(field, "="); name {
|
||||
case "upload":
|
||||
si.Upload, err = strconv.ParseInt(value, 10, 64)
|
||||
case "download":
|
||||
si.Download, err = strconv.ParseInt(value, 10, 64)
|
||||
case "total":
|
||||
si.Total, err = strconv.ParseInt(value, 10, 64)
|
||||
case "expire":
|
||||
si.Expire, err = strconv.ParseInt(value, 10, 64)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func str2uint64(str string) (int64, error) {
|
||||
i, err := strconv.ParseInt(str, 10, 64)
|
||||
return i, err
|
||||
}
|
||||
|
@ -11,10 +11,9 @@ type Bool struct {
|
||||
atomic.Bool
|
||||
}
|
||||
|
||||
func NewBool(val bool) *Bool {
|
||||
i := &Bool{}
|
||||
func NewBool(val bool) (i Bool) {
|
||||
i.Store(val)
|
||||
return i
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Bool) MarshalJSON() ([]byte, error) {
|
||||
@ -39,12 +38,11 @@ type Pointer[T any] struct {
|
||||
atomic.Pointer[T]
|
||||
}
|
||||
|
||||
func NewPointer[T any](v *T) *Pointer[T] {
|
||||
var p Pointer[T]
|
||||
func NewPointer[T any](v *T) (p Pointer[T]) {
|
||||
if v != nil {
|
||||
p.Store(v)
|
||||
}
|
||||
return &p
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Pointer[T]) MarshalJSON() ([]byte, error) {
|
||||
@ -68,10 +66,9 @@ type Int32 struct {
|
||||
atomic.Int32
|
||||
}
|
||||
|
||||
func NewInt32(val int32) *Int32 {
|
||||
i := &Int32{}
|
||||
func NewInt32(val int32) (i Int32) {
|
||||
i.Store(val)
|
||||
return i
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Int32) MarshalJSON() ([]byte, error) {
|
||||
@ -96,10 +93,9 @@ type Int64 struct {
|
||||
atomic.Int64
|
||||
}
|
||||
|
||||
func NewInt64(val int64) *Int64 {
|
||||
i := &Int64{}
|
||||
func NewInt64(val int64) (i Int64) {
|
||||
i.Store(val)
|
||||
return i
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Int64) MarshalJSON() ([]byte, error) {
|
||||
@ -124,10 +120,9 @@ type Uint32 struct {
|
||||
atomic.Uint32
|
||||
}
|
||||
|
||||
func NewUint32(val uint32) *Uint32 {
|
||||
i := &Uint32{}
|
||||
func NewUint32(val uint32) (i Uint32) {
|
||||
i.Store(val)
|
||||
return i
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Uint32) MarshalJSON() ([]byte, error) {
|
||||
@ -152,10 +147,9 @@ type Uint64 struct {
|
||||
atomic.Uint64
|
||||
}
|
||||
|
||||
func NewUint64(val uint64) *Uint64 {
|
||||
i := &Uint64{}
|
||||
func NewUint64(val uint64) (i Uint64) {
|
||||
i.Store(val)
|
||||
return i
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Uint64) MarshalJSON() ([]byte, error) {
|
||||
@ -180,10 +174,9 @@ type Uintptr struct {
|
||||
atomic.Uintptr
|
||||
}
|
||||
|
||||
func NewUintptr(val uintptr) *Uintptr {
|
||||
i := &Uintptr{}
|
||||
func NewUintptr(val uintptr) (i Uintptr) {
|
||||
i.Store(val)
|
||||
return i
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Uintptr) MarshalJSON() ([]byte, error) {
|
||||
|
@ -51,8 +51,7 @@ func (t *TypedValue[T]) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTypedValue[T any](t T) *TypedValue[T] {
|
||||
v := &TypedValue[T]{}
|
||||
func NewTypedValue[T any](t T) (v TypedValue[T]) {
|
||||
v.Store(t)
|
||||
return v
|
||||
return
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ const BufferSize = buf.BufferSize
|
||||
type Buffer = buf.Buffer
|
||||
|
||||
var New = buf.New
|
||||
var NewPacket = buf.NewPacket
|
||||
var NewSize = buf.NewSize
|
||||
var With = buf.With
|
||||
var As = buf.As
|
||||
|
@ -10,7 +10,11 @@ import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func ParseCert(certificate, privateKey string) (tls.Certificate, error) {
|
||||
type Path interface {
|
||||
Resolve(path string) string
|
||||
}
|
||||
|
||||
func ParseCert(certificate, privateKey string, path Path) (tls.Certificate, error) {
|
||||
if certificate == "" && privateKey == "" {
|
||||
return newRandomTLSKeyPair()
|
||||
}
|
||||
@ -19,6 +23,8 @@ func ParseCert(certificate, privateKey string) (tls.Certificate, error) {
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
certificate = path.Resolve(certificate)
|
||||
privateKey = path.Resolve(privateKey)
|
||||
cert, loadErr := tls.LoadX509KeyPair(certificate, privateKey)
|
||||
if loadErr != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("parse certificate failed, maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error())
|
||||
|
8
common/util/manipulation.go
Normal file
8
common/util/manipulation.go
Normal file
@ -0,0 +1,8 @@
|
||||
package util
|
||||
|
||||
import "github.com/samber/lo"
|
||||
|
||||
func EmptyOr[T comparable](v T, def T) T {
|
||||
ret, _ := lo.Coalesce(v, def)
|
||||
return ret
|
||||
}
|
@ -70,6 +70,9 @@ func DialContext(ctx context.Context, network, address string, options ...Option
|
||||
}
|
||||
|
||||
func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) {
|
||||
if DefaultSocketHook != nil {
|
||||
return listenPacketHooked(ctx, network, address)
|
||||
}
|
||||
cfg := applyOptions(options...)
|
||||
|
||||
lc := &net.ListenConfig{}
|
||||
@ -110,6 +113,9 @@ func GetTcpConcurrent() bool {
|
||||
}
|
||||
|
||||
func dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt *option) (net.Conn, error) {
|
||||
if DefaultSocketHook != nil {
|
||||
return dialContextHooked(ctx, network, destination, port)
|
||||
}
|
||||
address := net.JoinHostPort(destination.String(), port)
|
||||
|
||||
netDialer := opt.netDialer
|
||||
|
37
component/dialer/patch.go
Normal file
37
component/dialer/patch.go
Normal file
@ -0,0 +1,37 @@
|
||||
package dialer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type SocketControl func(network, address string, conn syscall.RawConn) error
|
||||
|
||||
var DefaultSocketHook SocketControl
|
||||
|
||||
func dialContextHooked(ctx context.Context, network string, destination netip.Addr, port string) (net.Conn, error) {
|
||||
dialer := &net.Dialer{
|
||||
Control: DefaultSocketHook,
|
||||
}
|
||||
|
||||
conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t, ok := conn.(*net.TCPConn); ok {
|
||||
t.SetKeepAlive(false)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) {
|
||||
lc := &net.ListenConfig{
|
||||
Control: DefaultSocketHook,
|
||||
}
|
||||
|
||||
return lc.ListenPacket(ctx, network, address)
|
||||
}
|
@ -7,6 +7,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
URL "net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -47,6 +48,7 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st
|
||||
|
||||
transport := &http.Transport{
|
||||
// from http.DefaultTransport
|
||||
DisableKeepAlives: runtime.GOOS == "android",
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
|
16
component/mmdb/patch.go
Normal file
16
component/mmdb/patch.go
Normal file
@ -0,0 +1,16 @@
|
||||
package mmdb
|
||||
|
||||
import "github.com/oschwald/maxminddb-golang"
|
||||
|
||||
func InstallOverride(override *maxminddb.Reader) {
|
||||
newReader := Reader{Reader: override}
|
||||
switch override.Metadata.DatabaseType {
|
||||
case "sing-geoip":
|
||||
reader.databaseType = typeSing
|
||||
case "Meta-geoip0":
|
||||
reader.databaseType = typeMetaV0
|
||||
default:
|
||||
reader.databaseType = typeMaxmind
|
||||
}
|
||||
reader = newReader
|
||||
}
|
14
component/process/patch.go
Normal file
14
component/process/patch.go
Normal file
@ -0,0 +1,14 @@
|
||||
package process
|
||||
|
||||
import "github.com/Dreamacro/clash/constant"
|
||||
|
||||
type PackageNameResolver func(metadata *constant.Metadata) (string, error)
|
||||
|
||||
var DefaultPackageNameResolver PackageNameResolver
|
||||
|
||||
func FindPackageName(metadata *constant.Metadata) (string, error) {
|
||||
if resolver := DefaultPackageNameResolver; resolver != nil {
|
||||
return resolver(metadata)
|
||||
}
|
||||
return "", ErrPlatformNotSupport
|
||||
}
|
@ -70,10 +70,7 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) (
|
||||
}
|
||||
|
||||
func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
|
||||
currentMeta := &C.Metadata{Type: C.INNER}
|
||||
if err := currentMeta.SetRemoteAddress(rAddrPort.String()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentMeta := &C.Metadata{Type: C.INNER, DstIP: rAddrPort.Addr(), DstPort: rAddrPort.Port()}
|
||||
return p.listenPacket(ctx, currentMeta)
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,10 @@ import (
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
const (
|
||||
minInterval = time.Minute * 5
|
||||
)
|
||||
|
||||
var (
|
||||
fileMode os.FileMode = 0o666
|
||||
dirMode os.FileMode = 0o755
|
||||
@ -24,8 +28,7 @@ type Fetcher[V any] struct {
|
||||
resourceType string
|
||||
name string
|
||||
vehicle types.Vehicle
|
||||
UpdatedAt *time.Time
|
||||
ticker *time.Ticker
|
||||
UpdatedAt time.Time
|
||||
done chan struct{}
|
||||
hash [16]byte
|
||||
parser Parser[V]
|
||||
@ -56,7 +59,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
||||
if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil {
|
||||
buf, err = os.ReadFile(f.vehicle.Path())
|
||||
modTime := stat.ModTime()
|
||||
f.UpdatedAt = &modTime
|
||||
f.UpdatedAt = modTime
|
||||
isLocal = true
|
||||
if f.interval != 0 && modTime.Add(f.interval).Before(time.Now()) {
|
||||
log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name())
|
||||
@ -64,6 +67,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
||||
}
|
||||
} else {
|
||||
buf, err = f.vehicle.Read()
|
||||
f.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -113,7 +117,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
|
||||
f.hash = md5.Sum(buf)
|
||||
|
||||
// pull contents automatically
|
||||
if f.ticker != nil {
|
||||
if f.interval > 0 {
|
||||
go f.pullLoop()
|
||||
}
|
||||
|
||||
@ -129,7 +133,7 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
|
||||
now := time.Now()
|
||||
hash := md5.Sum(buf)
|
||||
if bytes.Equal(f.hash[:], hash[:]) {
|
||||
f.UpdatedAt = &now
|
||||
f.UpdatedAt = now
|
||||
_ = os.Chtimes(f.vehicle.Path(), now, now)
|
||||
return lo.Empty[V](), true, nil
|
||||
}
|
||||
@ -145,23 +149,31 @@ func (f *Fetcher[V]) Update() (V, bool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
f.UpdatedAt = &now
|
||||
f.UpdatedAt = now
|
||||
f.hash = hash
|
||||
|
||||
return contents, false, nil
|
||||
}
|
||||
|
||||
func (f *Fetcher[V]) Destroy() error {
|
||||
if f.ticker != nil {
|
||||
if f.interval > 0 {
|
||||
f.done <- struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Fetcher[V]) pullLoop() {
|
||||
initialInterval := f.interval - time.Since(f.UpdatedAt)
|
||||
if initialInterval < minInterval {
|
||||
initialInterval = minInterval
|
||||
}
|
||||
|
||||
timer := time.NewTimer(initialInterval)
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-f.ticker.C:
|
||||
case <-timer.C:
|
||||
timer.Reset(f.interval)
|
||||
elm, same, err := f.Update()
|
||||
if err != nil {
|
||||
log.Errorln("[Provider] %s pull error: %s", f.Name(), err.Error())
|
||||
@ -178,7 +190,6 @@ func (f *Fetcher[V]) pullLoop() {
|
||||
f.OnUpdate(elm)
|
||||
}
|
||||
case <-f.done:
|
||||
f.ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -197,17 +208,12 @@ func safeWrite(path string, buf []byte) error {
|
||||
}
|
||||
|
||||
func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] {
|
||||
var ticker *time.Ticker
|
||||
if interval != 0 {
|
||||
ticker = time.NewTicker(interval)
|
||||
}
|
||||
|
||||
return &Fetcher[V]{
|
||||
name: name,
|
||||
ticker: ticker,
|
||||
vehicle: vehicle,
|
||||
parser: parser,
|
||||
done: make(chan struct{}, 1),
|
||||
done: make(chan struct{}, 8),
|
||||
OnUpdate: onUpdate,
|
||||
interval: interval,
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ func (*BaseSniffer) Protocol() string {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// SniffTCP implements sniffer.Sniffer
|
||||
func (*BaseSniffer) SniffTCP(bytes []byte) (string, error) {
|
||||
// SniffData implements sniffer.Sniffer
|
||||
func (*BaseSniffer) SniffData(bytes []byte) (string, error) {
|
||||
return "", errors.New("TODO")
|
||||
}
|
||||
|
||||
|
@ -35,9 +35,40 @@ type SnifferDispatcher struct {
|
||||
parsePureIp bool
|
||||
}
|
||||
|
||||
func (sd *SnifferDispatcher) shouldOverride(metadata *C.Metadata) bool {
|
||||
return (metadata.Host == "" && sd.parsePureIp) ||
|
||||
sd.forceDomain.Has(metadata.Host) ||
|
||||
(metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping)
|
||||
}
|
||||
|
||||
func (sd *SnifferDispatcher) UDPSniff(packet C.PacketAdapter) bool {
|
||||
metadata := packet.Metadata()
|
||||
|
||||
if sd.shouldOverride(packet.Metadata()) {
|
||||
for sniffer, config := range sd.sniffers {
|
||||
if sniffer.SupportNetwork() == C.UDP || sniffer.SupportNetwork() == C.ALLNet {
|
||||
inWhitelist := sniffer.SupportPort(metadata.DstPort)
|
||||
overrideDest := config.OverrideDest
|
||||
|
||||
if inWhitelist {
|
||||
host, err := sniffer.SniffData(packet.Data())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
sd.replaceDomain(metadata, host, overrideDest)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// TCPSniff returns true if the connection is sniffed to have a domain
|
||||
func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool {
|
||||
if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Has(metadata.Host) || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) {
|
||||
if sd.shouldOverride(metadata) {
|
||||
inWhitelist := false
|
||||
overrideDest := false
|
||||
for sniffer, config := range sd.sniffers {
|
||||
@ -86,7 +117,8 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
|
||||
|
||||
func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string, overrideDest bool) {
|
||||
// show log early, since the following code may mutate `metadata.Host`
|
||||
log.Debugln("[Sniffer] Sniff TCP [%s]-->[%s] success, replace domain [%s]-->[%s]",
|
||||
log.Debugln("[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]",
|
||||
metadata.NetWork,
|
||||
metadata.SourceDetail(),
|
||||
metadata.RemoteAddress(),
|
||||
metadata.Host, host)
|
||||
@ -125,7 +157,7 @@ func (sd *SnifferDispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metad
|
||||
continue
|
||||
}
|
||||
|
||||
host, err := s.SniffTCP(bytes)
|
||||
host, err := s.SniffData(bytes)
|
||||
if err != nil {
|
||||
//log.Debugln("[Sniffer] [%s] Sniff data failed %s", s.Protocol(), metadata.DstIP)
|
||||
continue
|
||||
@ -194,6 +226,8 @@ func NewSniffer(name sniffer.Type, snifferConfig SnifferConfig) (sniffer.Sniffer
|
||||
return NewTLSSniffer(snifferConfig)
|
||||
case sniffer.HTTP:
|
||||
return NewHTTPSniffer(snifferConfig)
|
||||
case sniffer.QUIC:
|
||||
return NewQuicSniffer(snifferConfig)
|
||||
default:
|
||||
return nil, ErrorUnsupportedSniffer
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ func (http *HTTPSniffer) SupportNetwork() C.NetWork {
|
||||
return C.TCP
|
||||
}
|
||||
|
||||
func (http *HTTPSniffer) SniffTCP(bytes []byte) (string, error) {
|
||||
func (http *HTTPSniffer) SniffData(bytes []byte) (string, error) {
|
||||
domain, err := SniffHTTP(bytes)
|
||||
if err == nil {
|
||||
return *domain, nil
|
||||
|
@ -1,3 +1,287 @@
|
||||
package sniffer
|
||||
|
||||
//TODO
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/Dreamacro/clash/common/buf"
|
||||
"github.com/Dreamacro/clash/common/utils"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
"github.com/metacubex/quic-go/quicvarint"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// Modified from https://github.com/v2fly/v2ray-core/blob/master/common/protocol/quic/sniff.go
|
||||
|
||||
const (
|
||||
versionDraft29 uint32 = 0xff00001d
|
||||
version1 uint32 = 0x1
|
||||
)
|
||||
|
||||
var (
|
||||
quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}
|
||||
quicSalt = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
|
||||
errNotQuic = errors.New("not QUIC")
|
||||
errNotQuicInitial = errors.New("not QUIC initial packet")
|
||||
)
|
||||
|
||||
type QuicSniffer struct {
|
||||
*BaseSniffer
|
||||
}
|
||||
|
||||
func NewQuicSniffer(snifferConfig SnifferConfig) (*QuicSniffer, error) {
|
||||
ports := snifferConfig.Ports
|
||||
if len(ports) == 0 {
|
||||
ports = utils.IntRanges[uint16]{utils.NewRange[uint16](443, 443)}
|
||||
}
|
||||
return &QuicSniffer{
|
||||
BaseSniffer: NewBaseSniffer(ports, C.UDP),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (quic QuicSniffer) Protocol() string {
|
||||
return "quic"
|
||||
}
|
||||
|
||||
func (quic QuicSniffer) SupportNetwork() C.NetWork {
|
||||
return C.UDP
|
||||
}
|
||||
|
||||
func (quic QuicSniffer) SniffData(b []byte) (string, error) {
|
||||
buffer := buf.As(b)
|
||||
typeByte, err := buffer.ReadByte()
|
||||
if err != nil {
|
||||
return "", errNotQuic
|
||||
}
|
||||
isLongHeader := typeByte&0x80 > 0
|
||||
if !isLongHeader || typeByte&0x40 == 0 {
|
||||
return "", errNotQuicInitial
|
||||
}
|
||||
|
||||
vb, err := buffer.ReadBytes(4)
|
||||
if err != nil {
|
||||
return "", errNotQuic
|
||||
}
|
||||
|
||||
versionNumber := binary.BigEndian.Uint32(vb)
|
||||
|
||||
if versionNumber != 0 && typeByte&0x40 == 0 {
|
||||
return "", errNotQuic
|
||||
} else if versionNumber != versionDraft29 && versionNumber != version1 {
|
||||
return "", errNotQuic
|
||||
}
|
||||
|
||||
if (typeByte&0x30)>>4 != 0x0 {
|
||||
return "", errNotQuicInitial
|
||||
}
|
||||
|
||||
var destConnID []byte
|
||||
if l, err := buffer.ReadByte(); err != nil {
|
||||
return "", errNotQuic
|
||||
} else if destConnID, err = buffer.ReadBytes(int(l)); err != nil {
|
||||
return "", errNotQuic
|
||||
}
|
||||
|
||||
if l, err := buffer.ReadByte(); err != nil {
|
||||
return "", errNotQuic
|
||||
} else if _, err := buffer.ReadBytes(int(l)); err != nil {
|
||||
return "", errNotQuic
|
||||
}
|
||||
|
||||
tokenLen, err := quicvarint.Read(buffer)
|
||||
if err != nil || tokenLen > uint64(len(b)) {
|
||||
return "", errNotQuic
|
||||
}
|
||||
|
||||
if _, err = buffer.ReadBytes(int(tokenLen)); err != nil {
|
||||
return "", errNotQuic
|
||||
}
|
||||
|
||||
packetLen, err := quicvarint.Read(buffer)
|
||||
if err != nil {
|
||||
return "", errNotQuic
|
||||
}
|
||||
|
||||
hdrLen := len(b) - buffer.Len()
|
||||
|
||||
var salt []byte
|
||||
if versionNumber == version1 {
|
||||
salt = quicSalt
|
||||
} else {
|
||||
salt = quicSaltOld
|
||||
}
|
||||
initialSecret := hkdf.Extract(crypto.SHA256.New, destConnID, salt)
|
||||
secret := hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size())
|
||||
hpKey := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic hp", 16)
|
||||
block, err := aes.NewCipher(hpKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cache := buf.NewPacket()
|
||||
defer cache.Release()
|
||||
|
||||
mask := cache.Extend(block.BlockSize())
|
||||
block.Encrypt(mask, b[hdrLen+4:hdrLen+4+16])
|
||||
firstByte := b[0]
|
||||
// Encrypt/decrypt first byte.
|
||||
if isLongHeader {
|
||||
// Long header: 4 bits masked
|
||||
// High 4 bits are not protected.
|
||||
firstByte ^= mask[0] & 0x0f
|
||||
} else {
|
||||
// Short header: 5 bits masked
|
||||
// High 3 bits are not protected.
|
||||
firstByte ^= mask[0] & 0x1f
|
||||
}
|
||||
packetNumberLength := int(firstByte&0x3 + 1) // max = 4 (64-bit sequence number)
|
||||
extHdrLen := hdrLen + packetNumberLength
|
||||
|
||||
// copy to avoid modify origin data
|
||||
extHdr := cache.Extend(extHdrLen)
|
||||
copy(extHdr, b)
|
||||
extHdr[0] = firstByte
|
||||
|
||||
packetNumber := extHdr[hdrLen:extHdrLen]
|
||||
// Encrypt/decrypt packet number.
|
||||
for i := range packetNumber {
|
||||
packetNumber[i] ^= mask[1+i]
|
||||
}
|
||||
|
||||
if packetNumber[0] != 0 && packetNumber[0] != 1 {
|
||||
return "", errNotQuicInitial
|
||||
}
|
||||
|
||||
data := b[extHdrLen : int(packetLen)+hdrLen]
|
||||
|
||||
key := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16)
|
||||
iv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12)
|
||||
aesCipher, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
aead, err := cipher.NewGCM(aesCipher)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// We only decrypt once, so we do not need to XOR it back.
|
||||
// https://github.com/quic-go/qtls-go1-20/blob/e132a0e6cb45e20ac0b705454849a11d09ba5a54/cipher_suites.go#L496
|
||||
for i, b := range packetNumber {
|
||||
iv[len(iv)-len(packetNumber)+i] ^= b
|
||||
}
|
||||
dst := cache.Extend(len(data))
|
||||
decrypted, err := aead.Open(dst[:0], iv, data, extHdr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buffer = buf.As(decrypted)
|
||||
|
||||
cryptoLen := uint(0)
|
||||
cryptoData := cache.Extend(buffer.Len())
|
||||
for i := 0; !buffer.IsEmpty(); i++ {
|
||||
frameType := byte(0x0) // Default to PADDING frame
|
||||
for frameType == 0x0 && !buffer.IsEmpty() {
|
||||
frameType, _ = buffer.ReadByte()
|
||||
}
|
||||
switch frameType {
|
||||
case 0x00: // PADDING frame
|
||||
case 0x01: // PING frame
|
||||
case 0x02, 0x03: // ACK frame
|
||||
if _, err = quicvarint.Read(buffer); err != nil { // Field: Largest Acknowledged
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Delay
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
ackRangeCount, err := quicvarint.Read(buffer) // Field: ACK Range Count
|
||||
if err != nil {
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
if _, err = quicvarint.Read(buffer); err != nil { // Field: First ACK Range
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
for i := 0; i < int(ackRangeCount); i++ { // Field: ACK Range
|
||||
if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> Gap
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> ACK Range Length
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
}
|
||||
if frameType == 0x03 {
|
||||
if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT0 Count
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT1 Count
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
if _, err = quicvarint.Read(buffer); err != nil { //nolint:misspell // Field: ECN Counts -> ECT-CE Count
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
}
|
||||
case 0x06: // CRYPTO frame, we will use this frame
|
||||
offset, err := quicvarint.Read(buffer) // Field: Offset
|
||||
if err != nil {
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
length, err := quicvarint.Read(buffer) // Field: Length
|
||||
if err != nil || length > uint64(buffer.Len()) {
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
if cryptoLen < uint(offset+length) {
|
||||
cryptoLen = uint(offset + length)
|
||||
}
|
||||
if _, err := buffer.Read(cryptoData[offset : offset+length]); err != nil { // Field: Crypto Data
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
case 0x1c: // CONNECTION_CLOSE frame, only 0x1c is permitted in initial packet
|
||||
if _, err = quicvarint.Read(buffer); err != nil { // Field: Error Code
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
if _, err = quicvarint.Read(buffer); err != nil { // Field: Frame Type
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
length, err := quicvarint.Read(buffer) // Field: Reason Phrase Length
|
||||
if err != nil {
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
if _, err := buffer.ReadBytes(int(length)); err != nil { // Field: Reason Phrase
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
default:
|
||||
// Only above frame types are permitted in initial packet.
|
||||
// See https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2.2-8
|
||||
return "", errNotQuicInitial
|
||||
}
|
||||
}
|
||||
|
||||
domain, err := ReadClientHello(cryptoData[:cryptoLen])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return *domain, nil
|
||||
}
|
||||
|
||||
func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte {
|
||||
b := make([]byte, 3, 3+6+len(label)+1+len(context))
|
||||
binary.BigEndian.PutUint16(b, uint16(length))
|
||||
b[2] = uint8(6 + len(label))
|
||||
b = append(b, []byte("tls13 ")...)
|
||||
b = append(b, []byte(label)...)
|
||||
b = b[:3+6+len(label)+1]
|
||||
b[3+6+len(label)] = uint8(len(context))
|
||||
b = append(b, context...)
|
||||
|
||||
out := make([]byte, length)
|
||||
n, err := hkdf.Expand(hash.New, secret, b).Read(out)
|
||||
if err != nil || n != length {
|
||||
panic("quic: HKDF-Expand-Label invocation failed unexpectedly")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
@ -1,9 +1,40 @@
|
||||
package sniffer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQuicHeaders(t *testing.T) {
|
||||
cases := []struct {
|
||||
input string
|
||||
domain string
|
||||
}{
|
||||
{
|
||||
input: "cd0000000108f1fb7bcc78aa5e7203a8f86400421531fe825b19541876db6c55c38890cd73149d267a084afee6087304095417a3033df6a81bbb71d8512e7a3e16df1e277cae5df3182cb214b8fe982ba3fdffbaa9ffec474547d55945f0fddbeadfb0b5243890b2fa3da45169e2bd34ec04b2e29382f48d612b28432a559757504d158e9e505407a77dd34f4b60b8d3b555ee85aacd6648686802f4de25e7216b19e54c5f78e8a5963380c742d861306db4c16e4f7fc94957aa50b9578a0b61f1e406b2ad5f0cd3cd271c4d99476409797b0c3cb3efec256118912d4b7e4fd79d9cb9016b6e5eaa4f5e57b637b217755daf8968a4092bed0ed5413f5d04904b3a61e4064f9211b2629e5b52a89c7b19f37a713e41e27743ea6dfa736dfa1bb0a4b2bc8c8dc632c6ce963493a20c550e6fdb2475213665e9a85cfc394da9cec0cf41f0c8abed3fc83be5245b2b5aa5e825d29349f721d30774ef5bf965b540f3d8d98febe20956b1fc8fa047e10e7d2f921c9c6622389e02322e80621a1cf5264e245b7276966eb02932584e3f7038bd36aa908766ad3fb98344025dec18670d6db43a1c5daac00937fce7b7c7d61ff4e6efd01a2bdee0ee183108b926393df4f3d74bbcbb015f240e7e346b7d01c41111a401225ce3b095ab4623a5836169bf9599eeca79d1d2e9b2202b5960a09211e978058d6fc0484eff3e91ce4649a5e3ba15b906d334cf66e28d9ff575406e1ae1ac2febafd72870b6f5d58fc5fb949cb1f40feb7c1d9ce5e71b",
|
||||
domain: "www.google.com",
|
||||
},
|
||||
{
|
||||
input: "c3000000011266f50524e8d0fe88cbf51e3ad71a13198235000044c82dc5d943fb34cc6d5c5e433610dc7a44f5951935c2c1d14ac641b02472340a892c4492dbfe3f8262109108fc36d96bdc1e9e46b5f1f6ef6104add2aafbfd8e79246eb3b4637541aaed7d195571724e642ab4d31c909f1db86e7d8516117ce8716bd1e3acb664c499086b0f3bc7258595420e7bb969f934457d195e832ffff4ffddf11123eeadacc48190e356c8f0f6abc381deb7e285e3b0613a795b19bddb9f002ffdf6fd70f0ff2072302b33d2421aac6540bb9f0e85c7237af0dd56225b2264d769160febab952e64bd5155f23e58c6113891143f946591032b41816aed3ac54f521f60605f86791de24c5765b664c1348cc53d5d631b4bbefe1915f2b21fefafb47badeb72d8ba1fd5c3cfeb0ba9d0112396f170e94cd33952c4fa87997b870931bf1a300e8e127f530815ff087815b4f9d004cbcd17013ac143847572a1655a5b36e054e8b9951d747c2c6ff25d7b2edb13a2a6b8074062332f2191f6830cf435a4ed9db5d9c4eb43a143bf3edf0c48f6f9435dafad4afb743a5a33990379df953ecd388e848aff0ebba9ccc052b8303c0bd1fee7e7553af1894e81b7772818bb69249540ccb8cfb47b1517abaf71c81c3bd271f1a5f1b66465f850f377c9db682b8e543c3d0c10fcd2dee263630889b7d1d521d1d27e866ea4ab5f43790d6a7f76ceefd5783678ca92cc131fa42fc4a01e2a81cad734ddf17a53e1bda8e0a21afc9e8c1118c9459b13519f5b3c3d9692c92234f01129d47ae8ec70625170847472801190b46d36f73b868f55f5a18a3cb05af6d38610e0829e4fbf13ddcc202341702e43dcf33be76ff4afe327e5783287c137aad075752940b41e7d9f5146e36d908897c6d7a9fdc343fde2d9c9d6e6a6b237669bd3e6abe0a732861a679eadfa29a876c6a646953c9361830811b012b26b31c9e7158f8de9c9a108346ddee3dd3886da6258364c1281bff8e055f6384e3a23e198b5e6b726fa7f811b3338072019d4b5fd05891770d11e3ed6ab5f7ed33db1c6220c5aa8fa1909949ac55d5435b75982e17aa80940fa574f0aba4dc340129cad491fdf1f5e05c4e83e36ad29ff38f15e1c9436c792024442f57f07583d671dd05446c84ea20b471303f6ae4e5e13f244d671e0ebe94d3d5c17d3f3f378cdd51fa8a6d2c977c78a2397dd1e251cd979803d617d45f575e5d9db0a28b3c4c25fe2af24af5bddac09786b6d6d8aa19cfbd5409bdbfed7d518ef5c863f3ee757bd9d37cddc546cc57d2e52b6ae58789f297a300f1d76c3842603eae4b1224de31a939a68875c86e697aeebf7ebc65568f43fc681bacab830ac4a2164d324e90067125bad702192d01cb3cb3d2689ae681967e86fd7ac93a25cf2e905c88ca5ad7d11962f021754cf3f61224517bd3411d5b5a83955bcea79d702466d073a6eaadc1202b3693e555b051a5b19457023a01e7f943742bb7f5f8aeba8d4e363973aebdccfb12479619cfb93e833be702a307e796dc7431a48abd9b755b392c510b98cd20ef778e2ac88d6a04f23ba8a253d7eb7c13e0c88c3a21f7e23857c58704d139703a47e0965bf2dc8810dc36894ac1f3da73c155e271c106a718b2d184e4e5637c820fe909984642960edfc9e62ac50af5dd3feee6bc560ced7bda676d4e290c9c5916fad52180bbc83d3483e95c79bac15c209936f21042dc2b6253eefdac06e7f4745044eaa0acedabf1d1c8cd9402738",
|
||||
domain: "cloudflare-dns.com",
|
||||
},
|
||||
}
|
||||
q, err := NewQuicSniffer(SnifferConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, test := range cases {
|
||||
pkt, err := hex.DecodeString(test.input)
|
||||
assert.NoError(t, err)
|
||||
oriPkt := bytes.Clone(pkt)
|
||||
domain, err := q.SniffData(pkt)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.domain, domain)
|
||||
assert.Equal(t, oriPkt, pkt) // ensure input data not changed
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSHeaders(t *testing.T) {
|
||||
cases := []struct {
|
||||
input []byte
|
||||
@ -142,6 +173,7 @@ func TestTLSHeaders(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
input := bytes.Clone(test.input)
|
||||
domain, err := SniffTLS(test.input)
|
||||
if test.err {
|
||||
if err == nil {
|
||||
@ -155,5 +187,6 @@ func TestTLSHeaders(t *testing.T) {
|
||||
t.Error("expect domain ", test.domain, " but got ", domain)
|
||||
}
|
||||
}
|
||||
assert.Equal(t, input, test.input)
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func (tls *TLSSniffer) SupportNetwork() C.NetWork {
|
||||
return C.TCP
|
||||
}
|
||||
|
||||
func (tls *TLSSniffer) SniffTCP(bytes []byte) (string, error) {
|
||||
func (tls *TLSSniffer) SniffData(bytes []byte) (string, error) {
|
||||
domain, err := SniffTLS(bytes)
|
||||
if err == nil {
|
||||
return *domain, nil
|
||||
|
@ -99,10 +99,9 @@ func copyConfig(c *tls.Config) *utls.Config {
|
||||
}
|
||||
}
|
||||
|
||||
// WebsocketHandshake basically calls UConn.Handshake inside it but it will only send
|
||||
// http/1.1 in its ALPN.
|
||||
// BuildWebsocketHandshakeState it will only send http/1.1 in its ALPN.
|
||||
// Copy from https://github.com/XTLS/Xray-core/blob/main/transport/internet/tls/tls.go
|
||||
func (c *UConn) WebsocketHandshake() error {
|
||||
func (c *UConn) BuildWebsocketHandshakeState() error {
|
||||
// Build the handshake state. This will apply every variable of the TLS of the
|
||||
// fingerprint in the UConn
|
||||
if err := c.BuildHandshakeState(); err != nil {
|
||||
@ -120,11 +119,11 @@ func (c *UConn) WebsocketHandshake() error {
|
||||
if !hasALPNExtension { // Append extension if doesn't exists
|
||||
c.Extensions = append(c.Extensions, &utls.ALPNExtension{AlpnProtocols: []string{"http/1.1"}})
|
||||
}
|
||||
// Rebuild the client hello and do the handshake
|
||||
// Rebuild the client hello
|
||||
if err := c.BuildHandshakeState(); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Handshake()
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetGlobalUtlsClient(Client string) {
|
||||
|
157
config/config.go
157
config/config.go
@ -59,27 +59,27 @@ type General struct {
|
||||
TCPConcurrent bool `json:"tcp-concurrent"`
|
||||
FindProcessMode P.FindProcessMode `json:"find-process-mode"`
|
||||
Sniffing bool `json:"sniffing"`
|
||||
EBpf EBpf `json:"-"`
|
||||
GlobalClientFingerprint string `json:"global-client-fingerprint"`
|
||||
GlobalUA string `json:"global-ua"`
|
||||
}
|
||||
|
||||
// Inbound config
|
||||
type Inbound struct {
|
||||
Port int `json:"port"`
|
||||
SocksPort int `json:"socks-port"`
|
||||
RedirPort int `json:"redir-port"`
|
||||
TProxyPort int `json:"tproxy-port"`
|
||||
MixedPort int `json:"mixed-port"`
|
||||
Tun LC.Tun `json:"tun"`
|
||||
TuicServer LC.TuicServer `json:"tuic-server"`
|
||||
ShadowSocksConfig string `json:"ss-config"`
|
||||
VmessConfig string `json:"vmess-config"`
|
||||
Authentication []string `json:"authentication"`
|
||||
AllowLan bool `json:"allow-lan"`
|
||||
BindAddress string `json:"bind-address"`
|
||||
InboundTfo bool `json:"inbound-tfo"`
|
||||
InboundMPTCP bool `json:"inbound-mptcp"`
|
||||
Port int `json:"port"`
|
||||
SocksPort int `json:"socks-port"`
|
||||
RedirPort int `json:"redir-port"`
|
||||
TProxyPort int `json:"tproxy-port"`
|
||||
MixedPort int `json:"mixed-port"`
|
||||
Tun LC.Tun `json:"tun"`
|
||||
TuicServer LC.TuicServer `json:"tuic-server"`
|
||||
ShadowSocksConfig string `json:"ss-config"`
|
||||
VmessConfig string `json:"vmess-config"`
|
||||
Authentication []string `json:"authentication"`
|
||||
SkipAuthPrefixes []netip.Prefix `json:"skip-auth-prefixes"`
|
||||
AllowLan bool `json:"allow-lan"`
|
||||
BindAddress string `json:"bind-address"`
|
||||
InboundTfo bool `json:"inbound-tfo"`
|
||||
InboundMPTCP bool `json:"inbound-mptcp"`
|
||||
}
|
||||
|
||||
// Controller config
|
||||
@ -159,6 +159,7 @@ type Sniffer struct {
|
||||
type Experimental struct {
|
||||
Fingerprints []string `yaml:"fingerprints"`
|
||||
QUICGoDisableGSO bool `yaml:"quic-go-disable-gso"`
|
||||
QUICGoDisableECN bool `yaml:"quic-go-disable-ecn"`
|
||||
}
|
||||
|
||||
// Config is clash config manager
|
||||
@ -192,29 +193,34 @@ type RawNTP struct {
|
||||
}
|
||||
|
||||
type RawDNS struct {
|
||||
Enable bool `yaml:"enable"`
|
||||
PreferH3 bool `yaml:"prefer-h3"`
|
||||
IPv6 bool `yaml:"ipv6"`
|
||||
IPv6Timeout uint `yaml:"ipv6-timeout"`
|
||||
UseHosts bool `yaml:"use-hosts"`
|
||||
NameServer []string `yaml:"nameserver"`
|
||||
Fallback []string `yaml:"fallback"`
|
||||
FallbackFilter RawFallbackFilter `yaml:"fallback-filter"`
|
||||
Listen string `yaml:"listen"`
|
||||
EnhancedMode C.DNSMode `yaml:"enhanced-mode"`
|
||||
FakeIPRange string `yaml:"fake-ip-range"`
|
||||
FakeIPFilter []string `yaml:"fake-ip-filter"`
|
||||
DefaultNameserver []string `yaml:"default-nameserver"`
|
||||
NameServerPolicy map[string]any `yaml:"nameserver-policy"`
|
||||
ProxyServerNameserver []string `yaml:"proxy-server-nameserver"`
|
||||
Enable bool `yaml:"enable" json:"enable"`
|
||||
PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"`
|
||||
IPv6 bool `yaml:"ipv6" json:"ipv6"`
|
||||
IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"`
|
||||
UseHosts bool `yaml:"use-hosts" json:"use-hosts"`
|
||||
NameServer []string `yaml:"nameserver" json:"nameserver"`
|
||||
Fallback []string `yaml:"fallback" json:"fallback"`
|
||||
FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"`
|
||||
Listen string `yaml:"listen" json:"listen"`
|
||||
EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"`
|
||||
FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"`
|
||||
FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"`
|
||||
DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"`
|
||||
NameServerPolicy map[string]any `yaml:"nameserver-policy" json:"nameserver-policy"`
|
||||
ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"`
|
||||
}
|
||||
|
||||
type RawFallbackFilter struct {
|
||||
GeoIP bool `yaml:"geoip"`
|
||||
GeoIPCode string `yaml:"geoip-code"`
|
||||
IPCIDR []string `yaml:"ipcidr"`
|
||||
Domain []string `yaml:"domain"`
|
||||
GeoSite []string `yaml:"geosite"`
|
||||
GeoIP bool `yaml:"geoip" json:"geoip"`
|
||||
GeoIPCode string `yaml:"geoip-code" json:"geoip-code"`
|
||||
IPCIDR []string `yaml:"ipcidr" json:"ipcidr"`
|
||||
Domain []string `yaml:"domain" json:"domain"`
|
||||
GeoSite []string `yaml:"geosite" json:"geosite"`
|
||||
}
|
||||
|
||||
type RawClashForAndroid struct {
|
||||
AppendSystemDNS bool `yaml:"append-system-dns" json:"append-system-dns"`
|
||||
UiSubtitlePattern string `yaml:"ui-subtitle-pattern" json:"ui-subtitle-pattern"`
|
||||
}
|
||||
|
||||
type RawTun struct {
|
||||
@ -227,21 +233,21 @@ type RawTun struct {
|
||||
RedirectToTun []string `yaml:"-" json:"-"`
|
||||
|
||||
MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
|
||||
//Inet4Address []LC.ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"`
|
||||
Inet6Address []LC.ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
|
||||
StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
|
||||
Inet4RouteAddress []LC.ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"`
|
||||
Inet6RouteAddress []LC.ListenPrefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"`
|
||||
IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
|
||||
IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
|
||||
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
|
||||
IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
|
||||
ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
|
||||
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
//Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4_address,omitempty"`
|
||||
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
|
||||
StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
|
||||
Inet4RouteAddress []netip.Prefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"`
|
||||
Inet6RouteAddress []netip.Prefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"`
|
||||
IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
|
||||
IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
|
||||
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
|
||||
IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
|
||||
ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
|
||||
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
}
|
||||
|
||||
type RawTuicServer struct {
|
||||
@ -260,22 +266,23 @@ type RawTuicServer struct {
|
||||
}
|
||||
|
||||
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"`
|
||||
Port int `yaml:"port" json:"port"`
|
||||
SocksPort int `yaml:"socks-port" json:"socks-port"`
|
||||
RedirPort int `yaml:"redir-port" json:"redir-port"`
|
||||
TProxyPort int `yaml:"tproxy-port" json:"tproxy-port"`
|
||||
MixedPort int `yaml:"mixed-port" json:"mixed-port"`
|
||||
ShadowSocksConfig string `yaml:"ss-config"`
|
||||
VmessConfig string `yaml:"vmess-config"`
|
||||
InboundTfo bool `yaml:"inbound-tfo"`
|
||||
InboundMPTCP bool `yaml:"inbound-mptcp"`
|
||||
Authentication []string `yaml:"authentication"`
|
||||
AllowLan bool `yaml:"allow-lan"`
|
||||
BindAddress string `yaml:"bind-address"`
|
||||
Mode T.TunnelMode `yaml:"mode"`
|
||||
UnifiedDelay bool `yaml:"unified-delay"`
|
||||
LogLevel log.LogLevel `yaml:"log-level"`
|
||||
IPv6 bool `yaml:"ipv6"`
|
||||
Authentication []string `yaml:"authentication" json:"authentication"`
|
||||
SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes"`
|
||||
AllowLan bool `yaml:"allow-lan" json:"allow-lan"`
|
||||
BindAddress string `yaml:"bind-address" json:"bind-address"`
|
||||
Mode T.TunnelMode `yaml:"mode" json:"mode"`
|
||||
UnifiedDelay bool `yaml:"unified-delay" json:"unified-delay"`
|
||||
LogLevel log.LogLevel `yaml:"log-level" json:"log-level"`
|
||||
IPv6 bool `yaml:"ipv6" json:"ipv6"`
|
||||
ExternalController string `yaml:"external-controller"`
|
||||
ExternalControllerTLS string `yaml:"external-controller-tls"`
|
||||
ExternalUI string `yaml:"external-ui"`
|
||||
@ -285,20 +292,20 @@ type RawConfig struct {
|
||||
Interface string `yaml:"interface-name"`
|
||||
RoutingMark int `yaml:"routing-mark"`
|
||||
Tunnels []LC.Tunnel `yaml:"tunnels"`
|
||||
GeodataMode bool `yaml:"geodata-mode"`
|
||||
GeodataLoader string `yaml:"geodata-loader"`
|
||||
GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"`
|
||||
GeodataLoader string `yaml:"geodata-loader" json:"geodata-loader"`
|
||||
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"`
|
||||
GlobalUA string `yaml:"global-ua"`
|
||||
KeepAliveInterval int `yaml:"keep-alive-interval"`
|
||||
|
||||
Sniffer RawSniffer `yaml:"sniffer"`
|
||||
Sniffer RawSniffer `yaml:"sniffer" json:"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"`
|
||||
Hosts map[string]any `yaml:"hosts" json:"hosts"`
|
||||
NTP RawNTP `yaml:"ntp" json:"ntp"`
|
||||
DNS RawDNS `yaml:"dns" json:"dns"`
|
||||
Tun RawTun `yaml:"tun"`
|
||||
TuicServer RawTuicServer `yaml:"tuic-server"`
|
||||
EBpf EBpf `yaml:"ebpf"`
|
||||
@ -312,6 +319,8 @@ type RawConfig struct {
|
||||
SubRules map[string][]string `yaml:"sub-rules"`
|
||||
RawTLS TLS `yaml:"tls"`
|
||||
Listeners []map[string]any `yaml:"listeners"`
|
||||
|
||||
ClashForAndroid RawClashForAndroid `yaml:"clash-for-android" json:"clash-for-android"`
|
||||
}
|
||||
|
||||
type GeoXUrl struct {
|
||||
@ -385,7 +394,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
||||
DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query
|
||||
AutoRoute: true,
|
||||
AutoDetectInterface: true,
|
||||
Inet6Address: []LC.ListenPrefix{LC.ListenPrefix(netip.MustParsePrefix("fdfe:dcba:9876::1/126"))},
|
||||
Inet6Address: []netip.Prefix{netip.MustParsePrefix("fdfe:dcba:9876::1/126")},
|
||||
},
|
||||
TuicServer: RawTuicServer{
|
||||
Enable: false,
|
||||
@ -479,7 +488,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
||||
startTime := time.Now()
|
||||
config.Experimental = &rawCfg.Experimental
|
||||
config.Profile = &rawCfg.Profile
|
||||
config.IPTables = &rawCfg.IPTables
|
||||
config.TLS = &rawCfg.RawTLS
|
||||
|
||||
general, err := parseGeneral(rawCfg)
|
||||
@ -540,11 +548,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
||||
}
|
||||
config.DNS = dnsCfg
|
||||
|
||||
err = parseTun(rawCfg.Tun, config.General)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = parseTuicServer(rawCfg.TuicServer, config.General)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -619,6 +622,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
||||
ShadowSocksConfig: cfg.ShadowSocksConfig,
|
||||
VmessConfig: cfg.VmessConfig,
|
||||
AllowLan: cfg.AllowLan,
|
||||
SkipAuthPrefixes: cfg.SkipAuthPrefixes,
|
||||
BindAddress: cfg.BindAddress,
|
||||
InboundTfo: cfg.InboundTfo,
|
||||
InboundMPTCP: cfg.InboundMPTCP,
|
||||
@ -640,7 +644,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
||||
GeodataLoader: cfg.GeodataLoader,
|
||||
TCPConcurrent: cfg.TCPConcurrent,
|
||||
FindProcessMode: cfg.FindProcessMode,
|
||||
EBpf: cfg.EBpf,
|
||||
GlobalClientFingerprint: cfg.GlobalClientFingerprint,
|
||||
GlobalUA: cfg.GlobalUA,
|
||||
}, nil
|
||||
@ -1360,7 +1363,7 @@ func parseTun(rawTun RawTun, general *General) error {
|
||||
RedirectToTun: rawTun.RedirectToTun,
|
||||
|
||||
MTU: rawTun.MTU,
|
||||
Inet4Address: []LC.ListenPrefix{LC.ListenPrefix(tunAddressPrefix)},
|
||||
Inet4Address: []netip.Prefix{tunAddressPrefix},
|
||||
Inet6Address: rawTun.Inet6Address,
|
||||
StrictRoute: rawTun.StrictRoute,
|
||||
Inet4RouteAddress: rawTun.Inet4RouteAddress,
|
||||
|
@ -252,6 +252,23 @@ type PacketAdapter interface {
|
||||
Metadata() *Metadata
|
||||
}
|
||||
|
||||
type packetAdapter struct {
|
||||
UDPPacket
|
||||
metadata *Metadata
|
||||
}
|
||||
|
||||
// Metadata returns destination metadata
|
||||
func (s *packetAdapter) Metadata() *Metadata {
|
||||
return s.metadata
|
||||
}
|
||||
|
||||
func NewPacketAdapter(packet UDPPacket, metadata *Metadata) PacketAdapter {
|
||||
return &packetAdapter{
|
||||
packet,
|
||||
metadata,
|
||||
}
|
||||
}
|
||||
|
||||
type WriteBack interface {
|
||||
WriteBack(b []byte, addr net.Addr) (n int, err error)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package constant
|
||||
|
||||
var (
|
||||
GeodataMode bool
|
||||
GeoIpUrl string
|
||||
MmdbUrl string
|
||||
GeoSiteUrl string
|
||||
GeoIpUrl = "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat"
|
||||
MmdbUrl = "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb"
|
||||
GeoSiteUrl = "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat"
|
||||
)
|
||||
|
@ -16,7 +16,7 @@ type MultiAddrListener interface {
|
||||
|
||||
type InboundListener interface {
|
||||
Name() string
|
||||
Listen(tcpIn chan<- ConnContext, udpIn chan<- PacketAdapter, natTable NatTable) error
|
||||
Listen(tunnel Tunnel) error
|
||||
Close() error
|
||||
Address() string
|
||||
RawAddress() string
|
||||
|
@ -147,6 +147,9 @@ type Metadata struct {
|
||||
SpecialProxy string `json:"specialProxy"`
|
||||
SpecialRules string `json:"specialRules"`
|
||||
RemoteDst string `json:"remoteDestination"`
|
||||
|
||||
RawSrcAddr net.Addr `json:"-"`
|
||||
RawDstAddr net.Addr `json:"-"`
|
||||
// Only domain rule
|
||||
SniffHost string `json:"sniffHost"`
|
||||
}
|
||||
@ -240,6 +243,34 @@ func (m *Metadata) Valid() bool {
|
||||
return m.Host != "" || m.DstIP.IsValid()
|
||||
}
|
||||
|
||||
func (m *Metadata) SetRemoteAddr(addr net.Addr) error {
|
||||
if addr == nil {
|
||||
return nil
|
||||
}
|
||||
if rawAddr, ok := addr.(interface{ RawAddr() net.Addr }); ok {
|
||||
if rawAddr := rawAddr.RawAddr(); rawAddr != nil {
|
||||
if err := m.SetRemoteAddr(rawAddr); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if addr, ok := addr.(interface{ AddrPort() netip.AddrPort }); ok { // *net.TCPAddr, *net.UDPAddr, M.Socksaddr
|
||||
if addrPort := addr.AddrPort(); addrPort.Port() != 0 {
|
||||
m.DstPort = addrPort.Port()
|
||||
if addrPort.IsValid() { // sing's M.Socksaddr maybe return an invalid AddrPort if it's a DomainName
|
||||
m.DstIP = addrPort.Addr().Unmap()
|
||||
return nil
|
||||
} else {
|
||||
if addr, ok := addr.(interface{ AddrString() string }); ok { // must be sing's M.Socksaddr
|
||||
m.Host = addr.AddrString() // actually is M.Socksaddr.Fqdn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.SetRemoteAddress(addr.String())
|
||||
}
|
||||
|
||||
func (m *Metadata) SetRemoteAddress(rawAddress string) error {
|
||||
host, port, err := net.SplitHostPort(rawAddress)
|
||||
if err != nil {
|
||||
|
@ -18,6 +18,9 @@ var (
|
||||
)
|
||||
|
||||
// Path is used to get the configuration path
|
||||
//
|
||||
// on Unix systems, `$HOME/.config/clash`.
|
||||
// on Windows, `%USERPROFILE%/.config/clash`.
|
||||
var Path = func() *path {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
@ -25,6 +28,13 @@ var Path = func() *path {
|
||||
}
|
||||
allowUnsafePath, _ := strconv.ParseBool(os.Getenv("SKIP_SAFE_PATH_CHECK"))
|
||||
homeDir = P.Join(homeDir, ".config", Name)
|
||||
|
||||
if _, err = os.Stat(homeDir); err != nil {
|
||||
if configHome, ok := os.LookupEnv("XDG_CONFIG_HOME"); ok {
|
||||
homeDir = P.Join(configHome, Name)
|
||||
}
|
||||
}
|
||||
|
||||
return &path{homeDir: homeDir, configFile: "config.yaml", allowUnsafePath: allowUnsafePath}
|
||||
}()
|
||||
|
||||
|
@ -4,7 +4,8 @@ import "github.com/Dreamacro/clash/constant"
|
||||
|
||||
type Sniffer interface {
|
||||
SupportNetwork() constant.NetWork
|
||||
SniffTCP(bytes []byte) (string, error)
|
||||
// SniffData must not change input bytes
|
||||
SniffData(bytes []byte) (string, error)
|
||||
Protocol() string
|
||||
SupportPort(port uint16) bool
|
||||
}
|
||||
@ -12,10 +13,11 @@ type Sniffer interface {
|
||||
const (
|
||||
TLS Type = iota
|
||||
HTTP
|
||||
QUIC
|
||||
)
|
||||
|
||||
var (
|
||||
List = []Type{TLS, HTTP}
|
||||
List = []Type{TLS, HTTP, QUIC}
|
||||
)
|
||||
|
||||
type Type int
|
||||
@ -26,6 +28,8 @@ func (rt Type) String() string {
|
||||
return "TLS"
|
||||
case HTTP:
|
||||
return "HTTP"
|
||||
case QUIC:
|
||||
return "QUIC"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
|
12
constant/tunnel.go
Normal file
12
constant/tunnel.go
Normal file
@ -0,0 +1,12 @@
|
||||
package constant
|
||||
|
||||
import "net"
|
||||
|
||||
type Tunnel interface {
|
||||
// HandleTCPConn will handle a tcp connection blocking
|
||||
HandleTCPConn(conn net.Conn, metadata *Metadata)
|
||||
// HandleUDPPacket will handle a udp packet nonblocking
|
||||
HandleUDPPacket(packet UDPPacket, metadata *Metadata)
|
||||
// NatTable return nat table
|
||||
NatTable() NatTable
|
||||
}
|
@ -23,7 +23,7 @@ type client struct {
|
||||
r *Resolver
|
||||
port string
|
||||
host string
|
||||
iface *atomic.TypedValue[string]
|
||||
iface atomic.TypedValue[string]
|
||||
proxyAdapter C.ProxyAdapter
|
||||
proxyName string
|
||||
addr string
|
||||
@ -77,8 +77,8 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
|
||||
network = "tcp"
|
||||
}
|
||||
|
||||
options := []dialer.Option{}
|
||||
if c.iface != nil && c.iface.Load() != "" {
|
||||
var options []dialer.Option
|
||||
if c.iface.Load() != "" {
|
||||
options = append(options, dialer.WithInterface(c.iface.Load()))
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
//go:build disabled
|
||||
// +build disabled
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
|
20
dns/local.go
Normal file
20
dns/local.go
Normal file
@ -0,0 +1,20 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type LocalServer struct {
|
||||
handler handler
|
||||
}
|
||||
|
||||
// ServeMsg implement resolver.LocalServer ResolveMsg
|
||||
func (s *LocalServer) ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) {
|
||||
return handlerWithContext(ctx, s.handler, msg)
|
||||
}
|
||||
|
||||
func NewLocalServer(resolver *Resolver, mapper *ResolverEnhancer) *LocalServer {
|
||||
return &LocalServer{handler: NewHandler(resolver, mapper)}
|
||||
}
|
73
dns/patch.go
73
dns/patch.go
@ -4,17 +4,76 @@ import (
|
||||
"context"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
|
||||
"github.com/Dreamacro/clash/common/cache"
|
||||
"github.com/Dreamacro/clash/component/dhcp"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
)
|
||||
|
||||
type LocalServer struct {
|
||||
handler handler
|
||||
const SystemDNSPlaceholder = "system"
|
||||
|
||||
var systemResolver *Resolver
|
||||
var isolateHandler handler
|
||||
|
||||
var _ dnsClient = (*dhcpClient)(nil)
|
||||
|
||||
type dhcpClient struct {
|
||||
enable bool
|
||||
}
|
||||
|
||||
// ServeMsg implement resolver.LocalServer ResolveMsg
|
||||
func (s *LocalServer) ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) {
|
||||
return handlerWithContext(ctx, s.handler, msg)
|
||||
func (d *dhcpClient) Address() string {
|
||||
return SystemDNSPlaceholder
|
||||
}
|
||||
|
||||
func NewLocalServer(resolver *Resolver, mapper *ResolverEnhancer) *LocalServer {
|
||||
return &LocalServer{handler: NewHandler(resolver, mapper)}
|
||||
func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) {
|
||||
return d.ExchangeContext(context.Background(), m)
|
||||
}
|
||||
|
||||
func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
|
||||
if s := systemResolver; s != nil {
|
||||
return s.ExchangeContext(ctx, m)
|
||||
}
|
||||
|
||||
return nil, dhcp.ErrNotFound
|
||||
}
|
||||
|
||||
func ServeDNSWithDefaultServer(msg *D.Msg) (*D.Msg, error) {
|
||||
if h := isolateHandler; h != nil {
|
||||
return handlerWithContext(context.Background(), h, msg)
|
||||
}
|
||||
|
||||
return nil, D.ErrTime
|
||||
}
|
||||
|
||||
func FlushCacheWithDefaultResolver() {
|
||||
if r := resolver.DefaultResolver; r != nil {
|
||||
r.(*Resolver).lruCache = cache.New[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true))
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateSystemDNS(addr []string) {
|
||||
if len(addr) == 0 {
|
||||
systemResolver = nil
|
||||
}
|
||||
|
||||
ns := make([]NameServer, 0, len(addr))
|
||||
for _, d := range addr {
|
||||
ns = append(ns, NameServer{Addr: d})
|
||||
}
|
||||
|
||||
systemResolver = NewResolver(Config{Main: ns})
|
||||
}
|
||||
|
||||
func UpdateIsolateHandler(resolver *Resolver, mapper *ResolverEnhancer) {
|
||||
if resolver == nil {
|
||||
isolateHandler = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
isolateHandler = NewHandler(resolver, mapper)
|
||||
}
|
||||
|
||||
func newDHCPClient(ifaceName string) *dhcpClient {
|
||||
return &dhcpClient{enable: ifaceName == SystemDNSPlaceholder}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"github.com/Dreamacro/clash/log"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
@ -194,13 +195,17 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
|
||||
msg := result.(*D.Msg)
|
||||
|
||||
if cache {
|
||||
putMsgToCache(r.lruCache, q.String(), msg)
|
||||
// OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files.
|
||||
msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool {
|
||||
return rr.Header().Rrtype != D.TypeOPT
|
||||
})
|
||||
putMsgToCache(r.lruCache, q.String(), q, msg)
|
||||
}
|
||||
}()
|
||||
|
||||
isIPReq := isIPRequest(q)
|
||||
if isIPReq {
|
||||
cache=true
|
||||
cache = true
|
||||
return r.ipExchange(ctx, m)
|
||||
}
|
||||
|
||||
@ -332,7 +337,10 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er
|
||||
res := <-msgCh
|
||||
if res.Error == nil {
|
||||
if ips := msgToIP(res.Msg); len(ips) != 0 {
|
||||
if !r.shouldIPFallback(ips[0]) {
|
||||
shouldNotFallback := lo.EveryBy(ips, func(ip netip.Addr) bool {
|
||||
return !r.shouldIPFallback(ip)
|
||||
})
|
||||
if shouldNotFallback {
|
||||
msg, err = res.Msg, res.Error // no need to wait for fallback result
|
||||
return
|
||||
}
|
||||
@ -394,7 +402,7 @@ func (r *Resolver) Invalid() bool {
|
||||
type NameServer struct {
|
||||
Net string
|
||||
Addr string
|
||||
Interface *atomic.TypedValue[string]
|
||||
Interface atomic.TypedValue[string]
|
||||
ProxyAdapter C.ProxyAdapter
|
||||
ProxyName string
|
||||
Params map[string]string
|
||||
|
@ -49,6 +49,7 @@ func (s *Server) SetHandler(handler handler) {
|
||||
}
|
||||
|
||||
func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) {
|
||||
UpdateIsolateHandler(resolver, mapper)
|
||||
if addr == address && resolver != nil {
|
||||
handler := NewHandler(resolver, mapper)
|
||||
server.SetHandler(handler)
|
||||
|
47
dns/util.go
47
dns/util.go
@ -29,14 +29,16 @@ const (
|
||||
MaxMsgSize = 65535
|
||||
)
|
||||
|
||||
const serverFailureCacheTTL uint32 = 5
|
||||
|
||||
func minimalTTL(records []D.RR) uint32 {
|
||||
minObj := lo.MinBy(records, func(r1 D.RR, r2 D.RR) bool {
|
||||
rr := lo.MinBy(records, func(r1 D.RR, r2 D.RR) bool {
|
||||
return r1.Header().Ttl < r2.Header().Ttl
|
||||
})
|
||||
if minObj != nil {
|
||||
return minObj.Header().Ttl
|
||||
if rr == nil {
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
return rr.Header().Ttl
|
||||
}
|
||||
|
||||
func updateTTL(records []D.RR, ttl uint32) {
|
||||
@ -49,28 +51,25 @@ func updateTTL(records []D.RR, ttl uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) {
|
||||
putMsgToCacheWithExpire(c, key, msg, 0)
|
||||
}
|
||||
|
||||
func putMsgToCacheWithExpire(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg, sec uint32) {
|
||||
if sec == 0 {
|
||||
if sec = minimalTTL(msg.Answer); sec == 0 {
|
||||
if sec = minimalTTL(msg.Ns); sec == 0 {
|
||||
sec = minimalTTL(msg.Extra)
|
||||
}
|
||||
}
|
||||
if sec == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if sec > 120 {
|
||||
sec = 120 // at least 2 minutes to cache
|
||||
}
|
||||
|
||||
func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, q D.Question, msg *D.Msg) {
|
||||
// skip dns cache for acme challenge
|
||||
if q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge.") {
|
||||
log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name)
|
||||
return
|
||||
}
|
||||
|
||||
c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Duration(sec)*time.Second))
|
||||
var ttl uint32
|
||||
if msg.Rcode == D.RcodeServerFailure {
|
||||
// [...] a resolver MAY cache a server failure response.
|
||||
// If it does so it MUST NOT cache it for longer than five (5) minutes [...]
|
||||
ttl = serverFailureCacheTTL
|
||||
} else {
|
||||
ttl = minimalTTL(append(append(msg.Answer, msg.Ns...), msg.Extra...))
|
||||
}
|
||||
if ttl == 0 {
|
||||
return
|
||||
}
|
||||
c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Duration(ttl)*time.Second))
|
||||
}
|
||||
|
||||
func setMsgTTL(msg *D.Msg, ttl uint32) {
|
||||
|
@ -8,6 +8,11 @@ mixed-port: 10801 # HTTP(S) 和 SOCKS 代理混合端口
|
||||
|
||||
allow-lan: true # 允许局域网连接
|
||||
bind-address: "*" # 绑定 IP 地址,仅作用于 allow-lan 为 true,'*'表示所有地址
|
||||
authentication: # http,socks入口的验证用户名,密码
|
||||
- "username:password"
|
||||
skip-auth-prefixes: # 设置跳过验证的IP段
|
||||
- 127.0.0.1/8
|
||||
- ::1/128
|
||||
|
||||
# find-process-mode has 3 values:always, strict, off
|
||||
# - always, 开启,强制匹配所有进程
|
||||
@ -137,7 +142,9 @@ sniffer:
|
||||
# 是否使用嗅探结果作为实际访问,默认 true
|
||||
# 全局配置,优先级低于 sniffer.sniff 实际配置
|
||||
override-destination: false
|
||||
sniff: # TLS 默认如果不配置 ports 默认嗅探 443
|
||||
sniff: # TLS 和 QUIC 默认如果不配置 ports 默认嗅探 443
|
||||
QUIC:
|
||||
# ports: [ 443 ]
|
||||
TLS:
|
||||
# ports: [443, 8443]
|
||||
|
||||
@ -936,6 +943,10 @@ listeners:
|
||||
- username: 1
|
||||
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
||||
alterId: 1
|
||||
# ws-path: "/" # 如果不为空则开启websocket传输层
|
||||
# 下面两项如果填写则开启tls(需要同时填写)
|
||||
# certificate: ./server.crt
|
||||
# private-key: ./server.key
|
||||
|
||||
- name: tuic-in-1
|
||||
type: tuic
|
||||
|
44
go.mod
44
go.mod
@ -5,51 +5,51 @@ go 1.20
|
||||
require (
|
||||
github.com/3andne/restls-client-go v0.1.6
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
|
||||
github.com/cilium/ebpf v0.11.0
|
||||
github.com/cilium/ebpf v0.12.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/gobwas/ws v1.3.0
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a
|
||||
github.com/jpillora/backoff v1.0.0
|
||||
github.com/klauspost/cpuid/v2 v2.2.5
|
||||
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.20230909013832-033f6a2115cf
|
||||
github.com/metacubex/sing-quic v0.0.0-20230921160948-82175eb07a81
|
||||
github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b
|
||||
github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966
|
||||
github.com/metacubex/sing-shadowsocks v0.2.5
|
||||
github.com/metacubex/sing-shadowsocks2 v0.1.4
|
||||
github.com/metacubex/sing-tun v0.1.12
|
||||
github.com/metacubex/sing-tun v0.1.15-0.20231022153326-92d6e97f0700
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170
|
||||
github.com/miekg/dns v1.1.56
|
||||
github.com/mroth/weightedrand/v2 v2.1.0
|
||||
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/puzpuzpuz/xsync/v2 v2.5.1
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
||||
github.com/sagernet/sing v0.2.11
|
||||
github.com/sagernet/sing v0.2.14
|
||||
github.com/sagernet/sing-mux v0.1.3
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f
|
||||
github.com/samber/lo v1.38.1
|
||||
github.com/shirou/gopsutil/v3 v3.23.8
|
||||
github.com/shirou/gopsutil/v3 v3.23.9
|
||||
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/automaxprocs v1.5.3
|
||||
golang.org/x/crypto v0.13.0
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
|
||||
golang.org/x/net v0.15.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/sys v0.12.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sync v0.4.0
|
||||
golang.org/x/sys v0.13.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
lukechampine.com/blake3 v1.2.1
|
||||
@ -65,11 +65,12 @@ require (
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
|
||||
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
|
||||
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gaukas/godicttls v0.0.4 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
@ -78,14 +79,14 @@ require (
|
||||
github.com/klauspost/compress v1.16.7 // 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
|
||||
github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 // indirect
|
||||
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 // 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
|
||||
@ -99,10 +100,11 @@ require (
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
go.uber.org/mock v0.3.0 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20230921160249-edb949c9c140
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20231001053806-1230641572b9
|
||||
|
99
go.sum
99
go.sum
@ -14,8 +14,8 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
|
||||
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
|
||||
github.com/cilium/ebpf v0.12.0 h1:oQEuIQIXgYhe1v7sYUG0P9vtJTYZLLdA6tiQmrOB1mo=
|
||||
github.com/cilium/ebpf v0.12.0/go.mod h1:u9H29/Iq+8cy70YqI6p5pfADkFl3vdnV2qXDg5JL0Zo=
|
||||
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
|
||||
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -33,8 +33,8 @@ github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4Rfsap
|
||||
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
|
||||
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
|
||||
github.com/frankban/quicktest v1.14.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/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
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=
|
||||
@ -49,10 +49,14 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
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/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.3.0 h1:sbeU3Y4Qzlb+MOzIe6mQGf7QR4Hkv6ZD0qhGkBFL2O0=
|
||||
github.com/gobwas/ws v1.3.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
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=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
@ -65,8 +69,6 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
@ -93,24 +95,24 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
|
||||
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.20230909013832-033f6a2115cf h1:hflzPbb2M+3uUOZEVO72MKd2R62xEermoVaNhJOzBR8=
|
||||
github.com/metacubex/quic-go v0.38.1-0.20230909013832-033f6a2115cf/go.mod h1:7RCcKJJk1DMeNQQNnYKS+7FqftqPfG031oP8jrYRMw8=
|
||||
github.com/metacubex/sing v0.0.0-20230921160249-edb949c9c140 h1:qiTekhMDwY2vXARJx1D9EIEdtllbL7+ZBzHX9DQpWs4=
|
||||
github.com/metacubex/sing v0.0.0-20230921160249-edb949c9c140/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg=
|
||||
github.com/metacubex/sing-quic v0.0.0-20230921160948-82175eb07a81 h1:6g+ohVa8FQLXz/ATmped/4kWuK0HKvhy1hwzQXyF0EI=
|
||||
github.com/metacubex/sing-quic v0.0.0-20230921160948-82175eb07a81/go.mod h1:oGpQmqe5tj3sPdPWCNLbBoUSwqd+Z6SqVO7TlMNVnH4=
|
||||
github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 h1:npBvaPAT145UY8682AzpUMWpdIxJti/WPLjy7gCiYYs=
|
||||
github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8/go.mod h1:ZR6Gas7P1GcADCVBc1uOrA0bLQqDDyp70+63fD/BE2c=
|
||||
github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b h1:uZ++sW8yg7Fr/Wvmmrb/V+SfxvRs0iMC+2+u2bRmO8g=
|
||||
github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b/go.mod h1:4pe6cY+nAMFU/Uxn1rfnxNIowsaJGDQ3uyy4VuiPkP4=
|
||||
github.com/metacubex/sing v0.0.0-20231001053806-1230641572b9 h1:F0+IuW0tZ96QHEmrebXAdYnz7ab7Gz4l5yYC4g6Cg8k=
|
||||
github.com/metacubex/sing v0.0.0-20231001053806-1230641572b9/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg=
|
||||
github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966 h1:wbOsbU3kfD5LRuJIntJwEPmgGSQukof8CgLNypi8az8=
|
||||
github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966/go.mod h1:GU7g2AZesXItk4CspDP8Dc7eGtlA2GVDihyCwsUXRSo=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.1.4/go.mod h1:Qz028sLfdY3qxGRm9FDI+IM2Ae3ty2wR7HIzD/56h/k=
|
||||
github.com/metacubex/sing-tun v0.1.12 h1:Jgmz0k3ddRiJ8zfS4X7j6B/iSy6GnOdDEU0nhqiZcK4=
|
||||
github.com/metacubex/sing-tun v0.1.12/go.mod h1:X2P/H1HqXwqGcguGXWDVDhSS1GmDxVi13OmbtDedZ2M=
|
||||
github.com/metacubex/sing-tun v0.1.15-0.20231022153326-92d6e97f0700 h1:JToLa8cxHrd6tOUHWCg9YM+o/4MXmjgagG909itmnyE=
|
||||
github.com/metacubex/sing-tun v0.1.15-0.20231022153326-92d6e97f0700/go.mod h1:atkIOs6Y5NeUzstK5SBvnrFo4z1JLuORhEfQECEVUpI=
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 h1:FtupiyFkaVjFvRa7B/uDtRWg5BNsoyPC9MTev3sDasY=
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74/go.mod h1:8EWBZpc+qNvf5gmvjAtMHK1/DpcWqzfcBL842K00BsM=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28 h1:mXFpxfR/1nADh+GoT8maWEvc6LO6uatPsARD8WzUDMA=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20230611155257-1498ae315a28/go.mod h1:KrDPq/dE793jGIJw9kcIvjA/proAfU0IeU7WlMXW7rs=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170 h1:DBGA0hmrP4pVIwLiXUONdphjcppED+plmVaKf1oqkwk=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170/go.mod h1:/VbJfbdLnANE+SKXyMk/96sTRrD4GdFLh5mkegqqFcY=
|
||||
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
|
||||
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
|
||||
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
|
||||
@ -135,12 +137,12 @@ 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/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU=
|
||||
github.com/puzpuzpuz/xsync/v2 v2.5.1/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.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4/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=
|
||||
@ -162,8 +164,8 @@ 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=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
|
||||
github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE=
|
||||
github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ=
|
||||
github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E=
|
||||
github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
@ -197,7 +199,6 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
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/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,26 +209,25 @@ 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/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
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.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
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.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -235,36 +235,27 @@ golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-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.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
|
@ -118,7 +118,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
||||
}
|
||||
|
||||
func initInnerTcp() {
|
||||
inner.New(tunnel.TCPIn())
|
||||
inner.New(tunnel.Tunnel)
|
||||
}
|
||||
|
||||
func GetGeneral() *config.General {
|
||||
@ -140,6 +140,7 @@ func GetGeneral() *config.General {
|
||||
ShadowSocksConfig: ports.ShadowSocksConfig,
|
||||
VmessConfig: ports.VmessConfig,
|
||||
Authentication: authenticator,
|
||||
SkipAuthPrefixes: inbound.SkipAuthPrefixes(),
|
||||
AllowLan: listener.AllowLan(),
|
||||
BindAddress: listener.BindAddress(),
|
||||
},
|
||||
@ -157,34 +158,34 @@ func GetGeneral() *config.General {
|
||||
}
|
||||
|
||||
func updateListeners(general *config.General, listeners map[string]C.InboundListener, force bool) {
|
||||
tcpIn := tunnel.TCPIn()
|
||||
udpIn := tunnel.UDPIn()
|
||||
natTable := tunnel.NatTable()
|
||||
|
||||
listener.PatchInboundListeners(listeners, tcpIn, udpIn, natTable, true)
|
||||
listener.PatchInboundListeners(listeners, tunnel.Tunnel, true)
|
||||
if !force {
|
||||
return
|
||||
}
|
||||
|
||||
allowLan := general.AllowLan
|
||||
listener.SetAllowLan(allowLan)
|
||||
inbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)
|
||||
|
||||
bindAddress := general.BindAddress
|
||||
listener.SetBindAddress(bindAddress)
|
||||
listener.ReCreateHTTP(general.Port, tcpIn)
|
||||
listener.ReCreateSocks(general.SocksPort, tcpIn, udpIn)
|
||||
listener.ReCreateRedir(general.RedirPort, tcpIn, udpIn, natTable)
|
||||
listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tcpIn, udpIn)
|
||||
listener.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn, natTable)
|
||||
listener.ReCreateMixed(general.MixedPort, tcpIn, udpIn)
|
||||
listener.ReCreateShadowSocks(general.ShadowSocksConfig, tcpIn, udpIn)
|
||||
listener.ReCreateVmess(general.VmessConfig, tcpIn, udpIn)
|
||||
listener.ReCreateTuic(LC.TuicServer(general.TuicServer), tcpIn, udpIn)
|
||||
listener.ReCreateHTTP(general.Port, tunnel.Tunnel)
|
||||
listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)
|
||||
listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)
|
||||
// listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel)
|
||||
listener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel)
|
||||
listener.ReCreateMixed(general.MixedPort, tunnel.Tunnel)
|
||||
listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel)
|
||||
listener.ReCreateVmess(general.VmessConfig, tunnel.Tunnel)
|
||||
listener.ReCreateTuic(general.TuicServer, tunnel.Tunnel)
|
||||
}
|
||||
|
||||
func updateExperimental(c *config.Config) {
|
||||
if c.Experimental.QUICGoDisableGSO {
|
||||
_ = os.Setenv("QUIC_GO_DISABLE_GSO", "1")
|
||||
_ = os.Setenv("QUIC_GO_DISABLE_GSO", strconv.FormatBool(true))
|
||||
}
|
||||
if c.Experimental.QUICGoDisableECN {
|
||||
_ = os.Setenv("QUIC_GO_DISABLE_ECN", strconv.FormatBool(true))
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,7 +340,7 @@ func updateTun(general *config.General) {
|
||||
if general == nil {
|
||||
return
|
||||
}
|
||||
listener.ReCreateTun(LC.Tun(general.Tun), tunnel.TCPIn(), tunnel.UDPIn())
|
||||
listener.ReCreateTun(general.Tun, tunnel.Tunnel)
|
||||
listener.ReCreateRedirToTun(general.Tun.RedirectToTun)
|
||||
}
|
||||
|
||||
@ -367,7 +368,7 @@ func updateSniffer(sniffer *config.Sniffer) {
|
||||
}
|
||||
|
||||
func updateTunnels(tunnels []LC.Tunnel) {
|
||||
listener.PatchTunnel(tunnels, tunnel.TCPIn(), tunnel.UDPIn())
|
||||
listener.PatchTunnel(tunnels, tunnel.Tunnel)
|
||||
}
|
||||
|
||||
func updateGeneral(general *config.General) {
|
||||
|
@ -2,9 +2,11 @@ package route
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/Dreamacro/clash/config"
|
||||
@ -47,6 +49,7 @@ type configSchema struct {
|
||||
TcptunConfig *string `json:"tcptun-config"`
|
||||
UdptunConfig *string `json:"udptun-config"`
|
||||
AllowLan *bool `json:"allow-lan"`
|
||||
SkipAuthPrefixes *[]netip.Prefix `json:"skip-auth-prefixes"`
|
||||
BindAddress *string `json:"bind-address"`
|
||||
Mode *tunnel.TunnelMode `json:"mode"`
|
||||
LogLevel *log.LogLevel `json:"log-level"`
|
||||
@ -66,21 +69,21 @@ type tunSchema struct {
|
||||
//RedirectToTun []string `yaml:"-" json:"-"`
|
||||
|
||||
MTU *uint32 `yaml:"mtu" json:"mtu,omitempty"`
|
||||
//Inet4Address *[]config.ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
|
||||
Inet6Address *[]LC.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
|
||||
StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
Inet4RouteAddress *[]LC.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"`
|
||||
Inet6RouteAddress *[]LC.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"`
|
||||
IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"`
|
||||
IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
|
||||
ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
|
||||
ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
|
||||
IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"`
|
||||
IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"`
|
||||
ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"`
|
||||
EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
|
||||
UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
|
||||
FileDescriptor *int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
//Inet4Address *[]netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
|
||||
Inet6Address *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
|
||||
StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
Inet4RouteAddress *[]netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"`
|
||||
Inet6RouteAddress *[]netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"`
|
||||
IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"`
|
||||
IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
|
||||
ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
|
||||
ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
|
||||
IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"`
|
||||
IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"`
|
||||
ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"`
|
||||
EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
|
||||
UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
|
||||
FileDescriptor *int `yaml:"file-descriptor" json:"file-descriptor"`
|
||||
}
|
||||
|
||||
type tuicServerSchema struct {
|
||||
@ -231,6 +234,10 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
|
||||
P.SetAllowLan(*general.AllowLan)
|
||||
}
|
||||
|
||||
if general.SkipAuthPrefixes != nil {
|
||||
inbound.SetSkipAuthPrefixes(*general.SkipAuthPrefixes)
|
||||
}
|
||||
|
||||
if general.BindAddress != nil {
|
||||
P.SetBindAddress(*general.BindAddress)
|
||||
}
|
||||
@ -249,19 +256,15 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ports := P.GetPorts()
|
||||
|
||||
tcpIn := tunnel.TCPIn()
|
||||
udpIn := tunnel.UDPIn()
|
||||
natTable := tunnel.NatTable()
|
||||
|
||||
P.ReCreateHTTP(pointerOrDefault(general.Port, ports.Port), tcpIn)
|
||||
P.ReCreateSocks(pointerOrDefault(general.SocksPort, ports.SocksPort), tcpIn, udpIn)
|
||||
P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort), tcpIn, udpIn, natTable)
|
||||
P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn, natTable)
|
||||
P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn)
|
||||
P.ReCreateTun(pointerOrDefaultTun(general.Tun, P.LastTunConf), tcpIn, udpIn)
|
||||
P.ReCreateShadowSocks(pointerOrDefaultString(general.ShadowSocksConfig, ports.ShadowSocksConfig), tcpIn, udpIn)
|
||||
P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tcpIn, udpIn)
|
||||
P.ReCreateTuic(pointerOrDefaultTuicServer(general.TuicServer, P.LastTuicConf), tcpIn, udpIn)
|
||||
P.ReCreateHTTP(pointerOrDefault(general.Port, ports.Port), tunnel.Tunnel)
|
||||
P.ReCreateSocks(pointerOrDefault(general.SocksPort, ports.SocksPort), tunnel.Tunnel)
|
||||
P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort), tunnel.Tunnel)
|
||||
P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tunnel.Tunnel)
|
||||
P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tunnel.Tunnel)
|
||||
P.ReCreateTun(pointerOrDefaultTun(general.Tun, P.LastTunConf), tunnel.Tunnel)
|
||||
P.ReCreateShadowSocks(pointerOrDefaultString(general.ShadowSocksConfig, ports.ShadowSocksConfig), tunnel.Tunnel)
|
||||
P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tunnel.Tunnel)
|
||||
P.ReCreateTuic(pointerOrDefaultTuicServer(general.TuicServer, P.LastTuicConf), tunnel.Tunnel)
|
||||
|
||||
if general.Mode != nil {
|
||||
tunnel.SetMode(*general.Mode)
|
||||
|
@ -11,7 +11,8 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/gobwas/ws"
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
)
|
||||
|
||||
func connectionRouter() http.Handler {
|
||||
@ -23,13 +24,13 @@ func connectionRouter() http.Handler {
|
||||
}
|
||||
|
||||
func getConnections(w http.ResponseWriter, r *http.Request) {
|
||||
if !websocket.IsWebSocketUpgrade(r) {
|
||||
if !(r.Header.Get("Upgrade") == "websocket") {
|
||||
snapshot := statistic.DefaultManager.Snapshot()
|
||||
render.JSON(w, r, snapshot)
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
conn, _, _, err := ws.UpgradeHTTP(r, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -55,7 +56,7 @@ func getConnections(w http.ResponseWriter, r *http.Request) {
|
||||
return err
|
||||
}
|
||||
|
||||
return conn.WriteMessage(websocket.TextMessage, buf.Bytes())
|
||||
return wsutil.WriteMessage(conn, ws.StateServerSide, ws.OpText, buf.Bytes())
|
||||
}
|
||||
|
||||
if err := sendSnapshot(); err != nil {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"crypto/subtle"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
@ -21,7 +22,8 @@ import (
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/gobwas/ws"
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -29,12 +31,6 @@ var (
|
||||
serverAddr = ""
|
||||
|
||||
uiPath = ""
|
||||
|
||||
upgrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type Traffic struct {
|
||||
@ -112,7 +108,7 @@ func Start(addr string, tlsAddr string, secret string,
|
||||
|
||||
if len(tlsAddr) > 0 {
|
||||
go func() {
|
||||
c, err := CN.ParseCert(certificat, privateKey)
|
||||
c, err := CN.ParseCert(certificat, privateKey, C.Path)
|
||||
if err != nil {
|
||||
log.Errorln("External controller tls listen error: %s", err)
|
||||
return
|
||||
@ -166,7 +162,7 @@ func authentication(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
// Browser websocket not support custom header
|
||||
if websocket.IsWebSocketUpgrade(r) && r.URL.Query().Get("token") != "" {
|
||||
if r.Header.Get("Upgrade") == "websocket" && r.URL.Query().Get("token") != "" {
|
||||
token := r.URL.Query().Get("token")
|
||||
if !safeEuqal(token, serverSecret) {
|
||||
render.Status(r, http.StatusUnauthorized)
|
||||
@ -197,10 +193,10 @@ func hello(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func traffic(w http.ResponseWriter, r *http.Request) {
|
||||
var wsConn *websocket.Conn
|
||||
if websocket.IsWebSocketUpgrade(r) {
|
||||
var wsConn net.Conn
|
||||
if r.Header.Get("Upgrade") == "websocket" {
|
||||
var err error
|
||||
wsConn, err = upgrader.Upgrade(w, r, nil)
|
||||
wsConn, _, _, err = ws.UpgradeHTTP(r, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -230,7 +226,7 @@ func traffic(w http.ResponseWriter, r *http.Request) {
|
||||
_, err = w.Write(buf.Bytes())
|
||||
w.(http.Flusher).Flush()
|
||||
} else {
|
||||
err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes())
|
||||
err = wsutil.WriteMessage(wsConn, ws.StateServerSide, ws.OpText, buf.Bytes())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -240,10 +236,10 @@ func traffic(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func memory(w http.ResponseWriter, r *http.Request) {
|
||||
var wsConn *websocket.Conn
|
||||
if websocket.IsWebSocketUpgrade(r) {
|
||||
var wsConn net.Conn
|
||||
if r.Header.Get("Upgrade") == "websocket" {
|
||||
var err error
|
||||
wsConn, err = upgrader.Upgrade(w, r, nil)
|
||||
wsConn, _, _, err = ws.UpgradeHTTP(r, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -280,7 +276,7 @@ func memory(w http.ResponseWriter, r *http.Request) {
|
||||
_, err = w.Write(buf.Bytes())
|
||||
w.(http.Flusher).Flush()
|
||||
} else {
|
||||
err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes())
|
||||
err = wsutil.WriteMessage(wsConn, ws.StateServerSide, ws.OpText, buf.Bytes())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -293,6 +289,16 @@ type Log struct {
|
||||
Type string `json:"type"`
|
||||
Payload string `json:"payload"`
|
||||
}
|
||||
type LogStructuredField struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
type LogStructured struct {
|
||||
Time string `json:"time"`
|
||||
Level string `json:"level"`
|
||||
Message string `json:"message"`
|
||||
Fields []LogStructuredField `json:"fields"`
|
||||
}
|
||||
|
||||
func getLogs(w http.ResponseWriter, r *http.Request) {
|
||||
levelText := r.URL.Query().Get("level")
|
||||
@ -300,6 +306,12 @@ func getLogs(w http.ResponseWriter, r *http.Request) {
|
||||
levelText = "info"
|
||||
}
|
||||
|
||||
formatText := r.URL.Query().Get("format")
|
||||
isStructured := false
|
||||
if formatText == "structured" {
|
||||
isStructured = true
|
||||
}
|
||||
|
||||
level, ok := log.LogLevelMapping[levelText]
|
||||
if !ok {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
@ -307,10 +319,10 @@ func getLogs(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var wsConn *websocket.Conn
|
||||
if websocket.IsWebSocketUpgrade(r) {
|
||||
var wsConn net.Conn
|
||||
if r.Header.Get("Upgrade") == "websocket" {
|
||||
var err error
|
||||
wsConn, err = upgrader.Upgrade(w, r, nil)
|
||||
wsConn, _, _, err = ws.UpgradeHTTP(r, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -342,11 +354,26 @@ func getLogs(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
buf.Reset()
|
||||
|
||||
if err := json.NewEncoder(buf).Encode(Log{
|
||||
Type: logM.Type(),
|
||||
Payload: logM.Payload,
|
||||
}); err != nil {
|
||||
break
|
||||
if !isStructured {
|
||||
if err := json.NewEncoder(buf).Encode(Log{
|
||||
Type: logM.Type(),
|
||||
Payload: logM.Payload,
|
||||
}); err != nil {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
newLevel := logM.Type()
|
||||
if newLevel == "warning" {
|
||||
newLevel = "warn"
|
||||
}
|
||||
if err := json.NewEncoder(buf).Encode(LogStructured{
|
||||
Time: time.Now().Format(time.TimeOnly),
|
||||
Level: newLevel,
|
||||
Message: logM.Payload,
|
||||
Fields: []LogStructuredField{},
|
||||
}); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
@ -354,7 +381,7 @@ func getLogs(w http.ResponseWriter, r *http.Request) {
|
||||
_, err = w.Write(buf.Bytes())
|
||||
w.(http.Flusher).Flush()
|
||||
} else {
|
||||
err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes())
|
||||
err = wsutil.WriteMessage(wsConn, ws.StateServerSide, ws.OpText, buf.Bytes())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -43,7 +43,7 @@ func (l *Listener) SetLookupFunc(lookupFunc func(netip.AddrPort) (socks5.Addr, e
|
||||
l.lookupFunc = lookupFunc
|
||||
}
|
||||
|
||||
func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) {
|
||||
func (l *Listener) handleRedir(conn net.Conn, tunnel C.Tunnel) {
|
||||
if l.lookupFunc == nil {
|
||||
log.Errorln("[Auto Redirect] lookup function is nil")
|
||||
return
|
||||
@ -58,10 +58,10 @@ func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) {
|
||||
|
||||
N.TCPKeepAlive(conn)
|
||||
|
||||
in <- inbound.NewSocket(target, conn, C.REDIR, l.additions...)
|
||||
tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.REDIR, l.additions...))
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
|
||||
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
if len(additions) == 0 {
|
||||
additions = []inbound.Addition{
|
||||
inbound.WithInName("DEFAULT-REDIR"),
|
||||
@ -87,7 +87,7 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*
|
||||
}
|
||||
continue
|
||||
}
|
||||
go rl.handleRedir(c, in)
|
||||
go rl.handleRedir(c, tunnel)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -1,72 +1,19 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/netip"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type ListenPrefix netip.Prefix
|
||||
|
||||
func (p ListenPrefix) MarshalJSON() ([]byte, error) {
|
||||
prefix := netip.Prefix(p)
|
||||
if !prefix.IsValid() {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
return json.Marshal(prefix.String())
|
||||
}
|
||||
|
||||
func (p ListenPrefix) MarshalYAML() (interface{}, error) {
|
||||
prefix := netip.Prefix(p)
|
||||
if !prefix.IsValid() {
|
||||
return nil, nil
|
||||
}
|
||||
return prefix.String(), nil
|
||||
}
|
||||
|
||||
func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error {
|
||||
var value string
|
||||
err := json.Unmarshal(bytes, &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prefix, err := netip.ParsePrefix(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*p = ListenPrefix(prefix)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ListenPrefix) UnmarshalYAML(node *yaml.Node) error {
|
||||
var value string
|
||||
err := node.Decode(&value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prefix, err := netip.ParsePrefix(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*p = ListenPrefix(prefix)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p ListenPrefix) Build() netip.Prefix {
|
||||
return netip.Prefix(p)
|
||||
}
|
||||
|
||||
func StringSliceToListenPrefixSlice(ss []string) ([]ListenPrefix, error) {
|
||||
lps := make([]ListenPrefix, 0, len(ss))
|
||||
func StringSliceToNetipPrefixSlice(ss []string) ([]netip.Prefix, error) {
|
||||
lps := make([]netip.Prefix, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
prefix, err := netip.ParsePrefix(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lps = append(lps, ListenPrefix(prefix))
|
||||
lps = append(lps, prefix)
|
||||
}
|
||||
return lps, nil
|
||||
}
|
||||
@ -81,11 +28,11 @@ type Tun struct {
|
||||
RedirectToTun []string `yaml:"-" json:"-"`
|
||||
|
||||
MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
|
||||
Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
|
||||
Inet6Address []ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
|
||||
Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
|
||||
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
|
||||
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
Inet4RouteAddress []ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"`
|
||||
Inet6RouteAddress []ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"`
|
||||
Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"`
|
||||
Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"`
|
||||
IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"`
|
||||
IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
|
||||
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
|
||||
|
@ -11,9 +11,12 @@ type VmessUser struct {
|
||||
}
|
||||
|
||||
type VmessServer struct {
|
||||
Enable bool
|
||||
Listen string
|
||||
Users []VmessUser
|
||||
Enable bool
|
||||
Listen string
|
||||
Users []VmessUser
|
||||
WsPath string
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
}
|
||||
|
||||
func (t VmessServer) String() string {
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
func newClient(source net.Addr, in chan<- C.ConnContext, additions ...inbound.Addition) *http.Client {
|
||||
func newClient(srcConn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &http.Transport{
|
||||
// from http.DefaultTransport
|
||||
@ -32,7 +32,7 @@ func newClient(source net.Addr, in chan<- C.ConnContext, additions ...inbound.Ad
|
||||
|
||||
left, right := net.Pipe()
|
||||
|
||||
in <- inbound.NewHTTP(dstAddr, source, right, additions...)
|
||||
go tunnel.HandleTCPConn(inbound.NewHTTP(dstAddr, srcConn, right, additions...))
|
||||
|
||||
return left, nil
|
||||
},
|
||||
|
7
listener/http/patch.go
Normal file
7
listener/http/patch.go
Normal file
@ -0,0 +1,7 @@
|
||||
package http
|
||||
|
||||
import "net"
|
||||
|
||||
func (l *Listener) Listener() net.Listener {
|
||||
return l.listener
|
||||
}
|
@ -14,8 +14,8 @@ import (
|
||||
"github.com/Dreamacro/clash/log"
|
||||
)
|
||||
|
||||
func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool], additions ...inbound.Addition) {
|
||||
client := newClient(c.RemoteAddr(), in, additions...)
|
||||
func HandleConn(c net.Conn, tunnel C.Tunnel, cache *cache.LruCache[string, bool], additions ...inbound.Addition) {
|
||||
client := newClient(c, tunnel, additions...)
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
conn := N.NewBufferedConn(c)
|
||||
@ -48,7 +48,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[strin
|
||||
break // close connection
|
||||
}
|
||||
|
||||
in <- inbound.NewHTTPS(request, conn, additions...)
|
||||
tunnel.HandleTCPConn(inbound.NewHTTPS(request, conn, additions...))
|
||||
|
||||
return // hijack connection
|
||||
}
|
||||
@ -61,7 +61,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[strin
|
||||
request.RequestURI = ""
|
||||
|
||||
if isUpgradeRequest(request) {
|
||||
handleUpgrade(conn, request, in, additions...)
|
||||
handleUpgrade(conn, request, tunnel, additions...)
|
||||
|
||||
return // hijack connection
|
||||
}
|
||||
@ -100,6 +100,9 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[strin
|
||||
|
||||
func authenticate(request *http.Request, cache *cache.LruCache[string, bool]) *http.Response {
|
||||
authenticator := authStore.Authenticator()
|
||||
if inbound.SkipAuthRemoteAddress(request.RemoteAddr) {
|
||||
authenticator = nil
|
||||
}
|
||||
if authenticator != nil {
|
||||
credential := parseBasicProxyAuthorization(request)
|
||||
if credential == "" {
|
||||
|
@ -30,11 +30,11 @@ func (l *Listener) Close() error {
|
||||
return l.listener.Close()
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
|
||||
return NewWithAuthenticate(addr, in, true, additions...)
|
||||
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
return NewWithAuthenticate(addr, tunnel, true, additions...)
|
||||
}
|
||||
|
||||
func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool, additions ...inbound.Addition) (*Listener, error) {
|
||||
func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) {
|
||||
if len(additions) == 0 {
|
||||
additions = []inbound.Addition{
|
||||
inbound.WithInName("DEFAULT-HTTP"),
|
||||
@ -65,7 +65,10 @@ func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool
|
||||
}
|
||||
continue
|
||||
}
|
||||
go HandleConn(conn, in, c, additions...)
|
||||
if t, ok := conn.(*net.TCPConn); ok {
|
||||
t.SetKeepAlive(false)
|
||||
}
|
||||
go HandleConn(conn, tunnel, c, additions...)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -25,7 +25,7 @@ func isUpgradeRequest(req *http.Request) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
||||
func handleUpgrade(conn net.Conn, request *http.Request, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
defer conn.Close()
|
||||
|
||||
removeProxyHeaders(request.Header)
|
||||
@ -43,7 +43,7 @@ func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext
|
||||
|
||||
left, right := net.Pipe()
|
||||
|
||||
in <- inbound.NewHTTP(dstAddr, conn.RemoteAddr(), right, additions...)
|
||||
go tunnel.HandleTCPConn(inbound.NewHTTP(dstAddr, conn, right, additions...))
|
||||
|
||||
var bufferedLeft *N.BufferedConn
|
||||
if request.TLS != nil {
|
||||
|
@ -61,7 +61,7 @@ func (b *Base) RawAddress() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (*Base) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (*Base) Listen(tunnel C.Tunnel) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -42,9 +42,9 @@ func (h *HTTP) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (h *HTTP) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (h *HTTP) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
h.l, err = http.New(h.RawAddress(), tcpIn, h.Additions()...)
|
||||
h.l, err = http.New(h.RawAddress(), tunnel, h.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -77,9 +77,9 @@ func (t *Hysteria2) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (t *Hysteria2) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (t *Hysteria2) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
t.l, err = sing_hysteria2.New(t.ts, tcpIn, udpIn, t.Additions()...)
|
||||
t.l, err = sing_hysteria2.New(t.ts, tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -50,14 +50,14 @@ func (m *Mixed) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (m *Mixed) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (m *Mixed) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
m.l, err = mixed.New(m.RawAddress(), tcpIn, m.Additions()...)
|
||||
m.l, err = mixed.New(m.RawAddress(), tunnel, m.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m.udp {
|
||||
m.lUDP, err = socks.NewUDP(m.RawAddress(), udpIn, m.Additions()...)
|
||||
m.lUDP, err = socks.NewUDP(m.RawAddress(), tunnel, m.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -42,9 +42,9 @@ func (r *Redir) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (r *Redir) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (r *Redir) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
r.l, err = redir.New(r.RawAddress(), tcpIn, r.Additions()...)
|
||||
r.l, err = redir.New(r.RawAddress(), tunnel, r.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -59,9 +59,9 @@ func (s *ShadowSocks) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (s *ShadowSocks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (s *ShadowSocks) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
s.l, err = sing_shadowsocks.New(s.ss, tcpIn, udpIn, s.Additions()...)
|
||||
s.l, err = sing_shadowsocks.New(s.ss, tunnel, s.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -68,13 +68,13 @@ func (s *Socks) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (s *Socks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (s *Socks) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
if s.stl, err = socks.New(s.RawAddress(), tcpIn, s.Additions()...); err != nil {
|
||||
if s.stl, err = socks.New(s.RawAddress(), tunnel, s.Additions()...); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.udp {
|
||||
if s.sul, err = socks.NewUDP(s.RawAddress(), udpIn, s.Additions()...); err != nil {
|
||||
if s.sul, err = socks.NewUDP(s.RawAddress(), tunnel, s.Additions()...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -49,14 +49,14 @@ func (t *TProxy) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (t *TProxy) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (t *TProxy) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
t.lTCP, err = tproxy.New(t.RawAddress(), tcpIn, t.Additions()...)
|
||||
t.lTCP, err = tproxy.New(t.RawAddress(), tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.udp {
|
||||
t.lUDP, err = tproxy.NewUDP(t.RawAddress(), udpIn, natTable, t.Additions()...)
|
||||
t.lUDP, err = tproxy.NewUDP(t.RawAddress(), tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -73,9 +73,9 @@ func (t *Tuic) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (t *Tuic) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (t *Tuic) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
t.l, err = tuic.New(t.ts, tcpIn, udpIn, t.Additions()...)
|
||||
t.l, err = tuic.New(t.ts, tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -56,19 +56,19 @@ func NewTun(options *TunOption) (*Tun, error) {
|
||||
if !exist {
|
||||
return nil, errors.New("invalid tun stack")
|
||||
}
|
||||
inet4Address, err := LC.StringSliceToListenPrefixSlice(options.Inet4Address)
|
||||
inet4Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet4Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inet6Address, err := LC.StringSliceToListenPrefixSlice(options.Inet6Address)
|
||||
inet6Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet6Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inet4RouteAddress, err := LC.StringSliceToListenPrefixSlice(options.Inet4RouteAddress)
|
||||
inet4RouteAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet4RouteAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inet6RouteAddress, err := LC.StringSliceToListenPrefixSlice(options.Inet6RouteAddress)
|
||||
inet6RouteAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet6RouteAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -113,9 +113,9 @@ func (t *Tun) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (t *Tun) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (t *Tun) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
t.l, err = sing_tun.New(t.tun, tcpIn, udpIn, t.Additions()...)
|
||||
t.l, err = sing_tun.New(t.tun, tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/listener/tunnel"
|
||||
LT "github.com/Dreamacro/clash/listener/tunnel"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
)
|
||||
|
||||
@ -21,8 +21,8 @@ func (o TunnelOption) Equal(config C.InboundConfig) bool {
|
||||
type Tunnel struct {
|
||||
*Base
|
||||
config *TunnelOption
|
||||
ttl *tunnel.Listener
|
||||
tul *tunnel.PacketConn
|
||||
ttl *LT.Listener
|
||||
tul *LT.PacketConn
|
||||
}
|
||||
|
||||
func NewTunnel(options *TunnelOption) (*Tunnel, error) {
|
||||
@ -74,16 +74,16 @@ func (t *Tunnel) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (t *Tunnel) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (t *Tunnel) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
for _, network := range t.config.Network {
|
||||
switch network {
|
||||
case "tcp":
|
||||
if t.ttl, err = tunnel.New(t.RawAddress(), t.config.Target, t.config.SpecialProxy, tcpIn, t.Additions()...); err != nil {
|
||||
if t.ttl, err = LT.New(t.RawAddress(), t.config.Target, t.config.SpecialProxy, tunnel, t.Additions()...); err != nil {
|
||||
return err
|
||||
}
|
||||
case "udp":
|
||||
if t.tul, err = tunnel.NewUDP(t.RawAddress(), t.config.Target, t.config.SpecialProxy, udpIn, t.Additions()...); err != nil {
|
||||
if t.tul, err = LT.NewUDP(t.RawAddress(), t.config.Target, t.config.SpecialProxy, tunnel, t.Additions()...); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
|
@ -9,7 +9,10 @@ import (
|
||||
|
||||
type VmessOption struct {
|
||||
BaseOption
|
||||
Users []VmessUser `inbound:"users"`
|
||||
Users []VmessUser `inbound:"users"`
|
||||
WsPath string `inbound:"ws-path,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
}
|
||||
|
||||
type VmessUser struct {
|
||||
@ -46,9 +49,12 @@ func NewVmess(options *VmessOption) (*Vmess, error) {
|
||||
Base: base,
|
||||
config: options,
|
||||
vs: LC.VmessServer{
|
||||
Enable: true,
|
||||
Listen: base.RawAddress(),
|
||||
Users: users,
|
||||
Enable: true,
|
||||
Listen: base.RawAddress(),
|
||||
Users: users,
|
||||
WsPath: options.WsPath,
|
||||
Certificate: options.Certificate,
|
||||
PrivateKey: options.PrivateKey,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@ -69,7 +75,7 @@ func (v *Vmess) Address() string {
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (v *Vmess) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error {
|
||||
func (v *Vmess) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
users := make([]LC.VmessUser, len(v.config.Users))
|
||||
for i, v := range v.config.Users {
|
||||
@ -79,7 +85,7 @@ func (v *Vmess) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter,
|
||||
AlterID: v.AlterID,
|
||||
}
|
||||
}
|
||||
v.l, err = sing_vmess.New(v.vs, tcpIn, udpIn, v.Additions()...)
|
||||
v.l, err = sing_vmess.New(v.vs, tunnel, v.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -3,24 +3,43 @@ package inner
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
var tcpIn chan<- C.ConnContext
|
||||
var tunnel C.Tunnel
|
||||
|
||||
func New(in chan<- C.ConnContext) {
|
||||
tcpIn = in
|
||||
func New(t C.Tunnel) {
|
||||
tunnel = t
|
||||
}
|
||||
|
||||
func HandleTcp(address string) (conn net.Conn, err error) {
|
||||
if tcpIn == nil {
|
||||
if tunnel == nil {
|
||||
return nil, errors.New("tcp uninitialized")
|
||||
}
|
||||
// executor Parsed
|
||||
conn1, conn2 := net.Pipe()
|
||||
context := inbound.NewInner(conn2, address)
|
||||
tcpIn <- context
|
||||
|
||||
metadata := &C.Metadata{}
|
||||
metadata.NetWork = C.TCP
|
||||
metadata.Type = C.INNER
|
||||
metadata.DNSMode = C.DNSNormal
|
||||
metadata.Process = C.ClashName
|
||||
metadata.RawSrcAddr = conn1.RemoteAddr()
|
||||
metadata.RawDstAddr = conn1.LocalAddr()
|
||||
if h, port, err := net.SplitHostPort(address); err == nil {
|
||||
if port, err := strconv.ParseUint(port, 10, 16); err == nil {
|
||||
metadata.DstPort = uint16(port)
|
||||
}
|
||||
if ip, err := netip.ParseAddr(h); err == nil {
|
||||
metadata.DstIP = ip
|
||||
} else {
|
||||
metadata.Host = h
|
||||
}
|
||||
}
|
||||
|
||||
go tunnel.HandleTCPConn(conn2, metadata)
|
||||
return conn1, nil
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
"github.com/Dreamacro/clash/listener/socks"
|
||||
"github.com/Dreamacro/clash/listener/tproxy"
|
||||
"github.com/Dreamacro/clash/listener/tuic"
|
||||
"github.com/Dreamacro/clash/listener/tunnel"
|
||||
LT "github.com/Dreamacro/clash/listener/tunnel"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
|
||||
"github.com/samber/lo"
|
||||
@ -42,8 +42,8 @@ var (
|
||||
tproxyUDPListener *tproxy.UDPListener
|
||||
mixedListener *mixed.Listener
|
||||
mixedUDPLister *socks.UDPListener
|
||||
tunnelTCPListeners = map[string]*tunnel.Listener{}
|
||||
tunnelUDPListeners = map[string]*tunnel.PacketConn{}
|
||||
tunnelTCPListeners = map[string]*LT.Listener{}
|
||||
tunnelUDPListeners = map[string]*LT.PacketConn{}
|
||||
inboundListeners = map[string]C.InboundListener{}
|
||||
tunLister *sing_tun.Listener
|
||||
shadowSocksListener C.MultiAddrListener
|
||||
@ -112,7 +112,7 @@ func SetBindAddress(host string) {
|
||||
bindAddress = host
|
||||
}
|
||||
|
||||
func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) {
|
||||
func ReCreateHTTP(port int, tunnel C.Tunnel) {
|
||||
httpMux.Lock()
|
||||
defer httpMux.Unlock()
|
||||
|
||||
@ -137,7 +137,7 @@ func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) {
|
||||
return
|
||||
}
|
||||
|
||||
httpListener, err = http.New(addr, tcpIn)
|
||||
httpListener, err = http.New(addr, tunnel)
|
||||
if err != nil {
|
||||
log.Errorln("Start HTTP server error: %s", err.Error())
|
||||
return
|
||||
@ -146,7 +146,7 @@ func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) {
|
||||
log.Infoln("HTTP proxy listening at: %s", httpListener.Address())
|
||||
}
|
||||
|
||||
func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) {
|
||||
func ReCreateSocks(port int, tunnel C.Tunnel) {
|
||||
socksMux.Lock()
|
||||
defer socksMux.Unlock()
|
||||
|
||||
@ -188,12 +188,12 @@ func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAd
|
||||
return
|
||||
}
|
||||
|
||||
tcpListener, err := socks.New(addr, tcpIn)
|
||||
tcpListener, err := socks.New(addr, tunnel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
udpListener, err := socks.NewUDP(addr, udpIn)
|
||||
udpListener, err := socks.NewUDP(addr, tunnel)
|
||||
if err != nil {
|
||||
tcpListener.Close()
|
||||
return
|
||||
@ -205,7 +205,7 @@ func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAd
|
||||
log.Infoln("SOCKS proxy listening at: %s", socksListener.Address())
|
||||
}
|
||||
|
||||
func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) {
|
||||
func ReCreateRedir(port int, tunnel C.Tunnel) {
|
||||
redirMux.Lock()
|
||||
defer redirMux.Unlock()
|
||||
|
||||
@ -238,12 +238,12 @@ func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAd
|
||||
return
|
||||
}
|
||||
|
||||
redirListener, err = redir.New(addr, tcpIn)
|
||||
redirListener, err = redir.New(addr, tunnel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
redirUDPListener, err = tproxy.NewUDP(addr, udpIn, natTable)
|
||||
redirUDPListener, err = tproxy.NewUDP(addr, tunnel)
|
||||
if err != nil {
|
||||
log.Warnln("Failed to start Redir UDP Listener: %s", err)
|
||||
}
|
||||
@ -251,7 +251,7 @@ func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAd
|
||||
log.Infoln("Redirect proxy listening at: %s", redirListener.Address())
|
||||
}
|
||||
|
||||
func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) {
|
||||
func ReCreateShadowSocks(shadowSocksConfig string, tunnel C.Tunnel) {
|
||||
ssMux.Lock()
|
||||
defer ssMux.Unlock()
|
||||
|
||||
@ -292,7 +292,7 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u
|
||||
return
|
||||
}
|
||||
|
||||
listener, err := sing_shadowsocks.New(ssConfig, tcpIn, udpIn)
|
||||
listener, err := sing_shadowsocks.New(ssConfig, tunnel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -305,7 +305,7 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u
|
||||
return
|
||||
}
|
||||
|
||||
func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) {
|
||||
func ReCreateVmess(vmessConfig string, tunnel C.Tunnel) {
|
||||
vmessMux.Lock()
|
||||
defer vmessMux.Unlock()
|
||||
|
||||
@ -344,7 +344,7 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<-
|
||||
return
|
||||
}
|
||||
|
||||
listener, err := sing_vmess.New(vsConfig, tcpIn, udpIn)
|
||||
listener, err := sing_vmess.New(vsConfig, tunnel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -357,7 +357,7 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<-
|
||||
return
|
||||
}
|
||||
|
||||
func ReCreateTuic(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) {
|
||||
func ReCreateTuic(config LC.TuicServer, tunnel C.Tunnel) {
|
||||
tuicMux.Lock()
|
||||
defer func() {
|
||||
LastTuicConf = config
|
||||
@ -389,7 +389,7 @@ func ReCreateTuic(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<-
|
||||
return
|
||||
}
|
||||
|
||||
listener, err := tuic.New(config, tcpIn, udpIn)
|
||||
listener, err := tuic.New(config, tunnel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -402,7 +402,7 @@ func ReCreateTuic(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<-
|
||||
return
|
||||
}
|
||||
|
||||
func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) {
|
||||
func ReCreateTProxy(port int, tunnel C.Tunnel) {
|
||||
tproxyMux.Lock()
|
||||
defer tproxyMux.Unlock()
|
||||
|
||||
@ -435,12 +435,12 @@ func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketA
|
||||
return
|
||||
}
|
||||
|
||||
tproxyListener, err = tproxy.New(addr, tcpIn)
|
||||
tproxyListener, err = tproxy.New(addr, tunnel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tproxyUDPListener, err = tproxy.NewUDP(addr, udpIn, natTable)
|
||||
tproxyUDPListener, err = tproxy.NewUDP(addr, tunnel)
|
||||
if err != nil {
|
||||
log.Warnln("Failed to start TProxy UDP Listener: %s", err)
|
||||
}
|
||||
@ -448,7 +448,7 @@ func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketA
|
||||
log.Infoln("TProxy server listening at: %s", tproxyListener.Address())
|
||||
}
|
||||
|
||||
func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) {
|
||||
func ReCreateMixed(port int, tunnel C.Tunnel) {
|
||||
mixedMux.Lock()
|
||||
defer mixedMux.Unlock()
|
||||
|
||||
@ -489,12 +489,12 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAd
|
||||
return
|
||||
}
|
||||
|
||||
mixedListener, err = mixed.New(addr, tcpIn)
|
||||
mixedListener, err = mixed.New(addr, tunnel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mixedUDPLister, err = socks.NewUDP(addr, udpIn)
|
||||
mixedUDPLister, err = socks.NewUDP(addr, tunnel)
|
||||
if err != nil {
|
||||
mixedListener.Close()
|
||||
return
|
||||
@ -503,7 +503,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAd
|
||||
log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address())
|
||||
}
|
||||
|
||||
func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) {
|
||||
func ReCreateTun(tunConf LC.Tun, tunnel C.Tunnel) {
|
||||
tunMux.Lock()
|
||||
defer func() {
|
||||
LastTunConf = tunConf
|
||||
@ -531,7 +531,7 @@ func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.Pack
|
||||
return
|
||||
}
|
||||
|
||||
lister, err := sing_tun.New(tunConf, tcpIn, udpIn)
|
||||
lister, err := sing_tun.New(tunConf, tunnel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -573,7 +573,7 @@ func ReCreateRedirToTun(ifaceNames []string) {
|
||||
log.Infoln("Attached tc ebpf program to interfaces %v", tcProgram.RawNICs())
|
||||
}
|
||||
|
||||
func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<- C.PacketAdapter) {
|
||||
func ReCreateAutoRedir(ifaceNames []string, tunnel C.Tunnel) {
|
||||
autoRedirMux.Lock()
|
||||
defer autoRedirMux.Unlock()
|
||||
|
||||
@ -614,7 +614,7 @@ func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<-
|
||||
|
||||
addr := genAddr("*", C.TcpAutoRedirPort, true)
|
||||
|
||||
autoRedirListener, err = autoredir.New(addr, tcpIn)
|
||||
autoRedirListener, err = autoredir.New(addr, tunnel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -629,7 +629,7 @@ func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<-
|
||||
log.Infoln("Auto redirect proxy listening at: %s, attached tc ebpf program to interfaces %v", autoRedirListener.Address(), autoRedirProgram.RawNICs())
|
||||
}
|
||||
|
||||
func PatchTunnel(tunnels []LC.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) {
|
||||
func PatchTunnel(tunnels []LC.Tunnel, tunnel C.Tunnel) {
|
||||
tunnelMux.Lock()
|
||||
defer tunnelMux.Unlock()
|
||||
|
||||
@ -699,7 +699,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 := tunnel.New(elm.addr, elm.target, elm.proxy, tcpIn)
|
||||
l, err := LT.New(elm.addr, elm.target, elm.proxy, tunnel)
|
||||
if err != nil {
|
||||
log.Errorln("Start tunnel %s error: %s", elm.target, err.Error())
|
||||
continue
|
||||
@ -707,7 +707,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 := tunnel.NewUDP(elm.addr, elm.target, elm.proxy, udpIn)
|
||||
l, err := LT.NewUDP(elm.addr, elm.target, elm.proxy, tunnel)
|
||||
if err != nil {
|
||||
log.Errorln("Start tunnel %s error: %s", elm.target, err.Error())
|
||||
continue
|
||||
@ -718,7 +718,7 @@ func PatchTunnel(tunnels []LC.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- C
|
||||
}
|
||||
}
|
||||
|
||||
func PatchInboundListeners(newListenerMap map[string]C.InboundListener, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable, dropOld bool) {
|
||||
func PatchInboundListeners(newListenerMap map[string]C.InboundListener, tunnel C.Tunnel, dropOld bool) {
|
||||
inboundMux.Lock()
|
||||
defer inboundMux.Unlock()
|
||||
|
||||
@ -730,7 +730,7 @@ func PatchInboundListeners(newListenerMap map[string]C.InboundListener, tcpIn ch
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err := newListener.Listen(tcpIn, udpIn, natTable); err != nil {
|
||||
if err := newListener.Listen(tunnel); err != nil {
|
||||
log.Errorln("Listener %s listen err: %s", name, err.Error())
|
||||
continue
|
||||
}
|
||||
@ -834,19 +834,19 @@ func hasTunConfigChange(tunConf *LC.Tun) bool {
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.Inet4Address, func(i, j int) bool {
|
||||
return tunConf.Inet4Address[i].Build().String() < tunConf.Inet4Address[j].Build().String()
|
||||
return tunConf.Inet4Address[i].String() < tunConf.Inet4Address[j].String()
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.Inet6Address, func(i, j int) bool {
|
||||
return tunConf.Inet6Address[i].Build().String() < tunConf.Inet6Address[j].Build().String()
|
||||
return tunConf.Inet6Address[i].String() < tunConf.Inet6Address[j].String()
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.Inet4RouteAddress, func(i, j int) bool {
|
||||
return tunConf.Inet4RouteAddress[i].Build().String() < tunConf.Inet4RouteAddress[j].Build().String()
|
||||
return tunConf.Inet4RouteAddress[i].String() < tunConf.Inet4RouteAddress[j].String()
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.Inet6RouteAddress, func(i, j int) bool {
|
||||
return tunConf.Inet6RouteAddress[i].Build().String() < tunConf.Inet6RouteAddress[j].Build().String()
|
||||
return tunConf.Inet6RouteAddress[i].String() < tunConf.Inet6RouteAddress[j].String()
|
||||
})
|
||||
|
||||
sort.Slice(tunConf.IncludeUID, func(i, j int) bool {
|
||||
|
@ -36,7 +36,7 @@ func (l *Listener) Close() error {
|
||||
return l.listener.Close()
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
|
||||
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
if len(additions) == 0 {
|
||||
additions = []inbound.Addition{
|
||||
inbound.WithInName("DEFAULT-MIXED"),
|
||||
@ -62,14 +62,14 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*
|
||||
}
|
||||
continue
|
||||
}
|
||||
go handleConn(c, in, ml.cache, additions...)
|
||||
go handleConn(c, tunnel, ml.cache, additions...)
|
||||
}
|
||||
}()
|
||||
|
||||
return ml, nil
|
||||
}
|
||||
|
||||
func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool], additions ...inbound.Addition) {
|
||||
func handleConn(conn net.Conn, tunnel C.Tunnel, cache *cache.LruCache[string, bool], additions ...inbound.Addition) {
|
||||
N.TCPKeepAlive(conn)
|
||||
|
||||
bufConn := N.NewBufferedConn(conn)
|
||||
@ -80,10 +80,10 @@ func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[st
|
||||
|
||||
switch head[0] {
|
||||
case socks4.Version:
|
||||
socks.HandleSocks4(bufConn, in, additions...)
|
||||
socks.HandleSocks4(bufConn, tunnel, additions...)
|
||||
case socks5.Version:
|
||||
socks.HandleSocks5(bufConn, in, additions...)
|
||||
socks.HandleSocks5(bufConn, tunnel, additions...)
|
||||
default:
|
||||
http.HandleConn(bufConn, in, cache, additions...)
|
||||
http.HandleConn(bufConn, tunnel, cache, additions...)
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ func (l *Listener) Close() error {
|
||||
return l.listener.Close()
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
|
||||
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
if len(additions) == 0 {
|
||||
additions = []inbound.Addition{
|
||||
inbound.WithInName("DEFAULT-REDIR"),
|
||||
@ -55,18 +55,19 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*
|
||||
}
|
||||
continue
|
||||
}
|
||||
go handleRedir(c, in, additions...)
|
||||
go handleRedir(c, tunnel, additions...)
|
||||
}
|
||||
}()
|
||||
|
||||
return rl, nil
|
||||
}
|
||||
func handleRedir(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
||||
|
||||
func handleRedir(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
target, err := parserPacket(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
N.TCPKeepAlive(conn)
|
||||
in <- inbound.NewSocket(target, conn, C.REDIR, additions...)
|
||||
tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.REDIR, additions...))
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ type Listener struct {
|
||||
|
||||
var _listener *Listener
|
||||
|
||||
func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) {
|
||||
func New(config LC.ShadowsocksServer, tunnel C.Tunnel) (*Listener, error) {
|
||||
pickCipher, err := core.PickCipher(config.Cipher, nil, config.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -36,7 +36,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C
|
||||
|
||||
if config.Udp {
|
||||
//UDP
|
||||
ul, err := NewUDP(addr, pickCipher, udpIn)
|
||||
ul, err := NewUDP(addr, pickCipher, tunnel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -60,7 +60,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C
|
||||
continue
|
||||
}
|
||||
N.TCPKeepAlive(c)
|
||||
go sl.HandleConn(c, tcpIn)
|
||||
go sl.HandleConn(c, tunnel)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -99,7 +99,7 @@ func (l *Listener) AddrList() (addrList []net.Addr) {
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
||||
func (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
conn = l.pickCipher.StreamConn(conn)
|
||||
conn = N.NewDeadlineConn(conn) // embed ss can't handle readDeadline correctly
|
||||
|
||||
@ -108,12 +108,12 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions
|
||||
_ = conn.Close()
|
||||
return
|
||||
}
|
||||
in <- inbound.NewSocket(target, conn, C.SHADOWSOCKS, additions...)
|
||||
tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.SHADOWSOCKS, additions...))
|
||||
}
|
||||
|
||||
func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool {
|
||||
func HandleShadowSocks(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) bool {
|
||||
if _listener != nil && _listener.pickCipher != nil {
|
||||
go _listener.HandleConn(conn, in, additions...)
|
||||
go _listener.HandleConn(conn, tunnel, additions...)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -17,7 +17,7 @@ type UDPListener struct {
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewUDP(addr string, pickCipher core.Cipher, in chan<- C.PacketAdapter) (*UDPListener, error) {
|
||||
func NewUDP(addr string, pickCipher core.Cipher, tunnel C.Tunnel) (*UDPListener, error) {
|
||||
l, err := net.ListenPacket("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -42,7 +42,7 @@ func NewUDP(addr string, pickCipher core.Cipher, in chan<- C.PacketAdapter) (*UD
|
||||
}
|
||||
continue
|
||||
}
|
||||
handleSocksUDP(conn, in, data, put, remoteAddr)
|
||||
handleSocksUDP(conn, tunnel, data, put, remoteAddr)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -58,7 +58,7 @@ func (l *UDPListener) LocalAddr() net.Addr {
|
||||
return l.packetConn.LocalAddr()
|
||||
}
|
||||
|
||||
func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, put func(), addr net.Addr, additions ...inbound.Addition) {
|
||||
func handleSocksUDP(pc net.PacketConn, tunnel C.Tunnel, buf []byte, put func(), addr net.Addr, additions ...inbound.Addition) {
|
||||
tgtAddr := socks5.SplitAddr(buf)
|
||||
if tgtAddr == nil {
|
||||
// Unresolved UDP packet, return buffer to the pool
|
||||
@ -67,7 +67,7 @@ func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, pu
|
||||
}
|
||||
return
|
||||
}
|
||||
target := socks5.ParseAddr(tgtAddr.String())
|
||||
target := tgtAddr
|
||||
payload := buf[len(tgtAddr):]
|
||||
|
||||
packet := &packet{
|
||||
@ -76,8 +76,5 @@ func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, pu
|
||||
payload: payload,
|
||||
put: put,
|
||||
}
|
||||
select {
|
||||
case in <- inbound.NewPacket(target, packet, C.SHADOWSOCKS, additions...):
|
||||
default:
|
||||
}
|
||||
tunnel.HandleUDPPacket(inbound.NewPacket(target, packet, C.SHADOWSOCKS, additions...))
|
||||
}
|
||||
|
@ -17,28 +17,15 @@ func WithAdditions(ctx context.Context, additions ...inbound.Addition) context.C
|
||||
return context.WithValue(ctx, ctxKeyAdditions, additions)
|
||||
}
|
||||
|
||||
func getAdditions(ctx context.Context) []inbound.Addition {
|
||||
func getAdditions(ctx context.Context) (additions []inbound.Addition) {
|
||||
if v := ctx.Value(ctxKeyAdditions); v != nil {
|
||||
if a, ok := v.([]inbound.Addition); ok {
|
||||
return a
|
||||
additions = a
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func combineAdditions(ctx context.Context, additions []inbound.Addition) []inbound.Addition {
|
||||
additionsCloned := false
|
||||
if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 {
|
||||
additions = slices.Clone(additions)
|
||||
additionsCloned = true
|
||||
additions = append(additions, ctxAdditions...)
|
||||
}
|
||||
if user, ok := auth.UserFromContext[string](ctx); ok {
|
||||
if !additionsCloned {
|
||||
additions = slices.Clone(additions)
|
||||
additionsCloned = true
|
||||
}
|
||||
additions = slices.Clone(additions)
|
||||
additions = append(additions, inbound.WithInUser(user))
|
||||
}
|
||||
return additions
|
||||
return
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
N "github.com/Dreamacro/clash/common/net"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
|
||||
vmess "github.com/metacubex/sing-vmess"
|
||||
mux "github.com/sagernet/sing-mux"
|
||||
@ -28,43 +27,12 @@ import (
|
||||
const UDPTimeout = 5 * time.Minute
|
||||
|
||||
type ListenerHandler struct {
|
||||
TcpIn chan<- C.ConnContext
|
||||
UdpIn chan<- C.PacketAdapter
|
||||
Tunnel C.Tunnel
|
||||
Type C.Type
|
||||
Additions []inbound.Addition
|
||||
UDPTimeout time.Duration
|
||||
}
|
||||
|
||||
type waitCloseConn struct {
|
||||
N.ExtendedConn
|
||||
wg *sync.WaitGroup
|
||||
close sync.Once
|
||||
rAddr net.Addr
|
||||
}
|
||||
|
||||
func (c *waitCloseConn) Close() error { // call from handleTCPConn(connCtx C.ConnContext)
|
||||
c.close.Do(func() {
|
||||
c.wg.Done()
|
||||
})
|
||||
return c.ExtendedConn.Close()
|
||||
}
|
||||
|
||||
func (c *waitCloseConn) RemoteAddr() net.Addr {
|
||||
return c.rAddr
|
||||
}
|
||||
|
||||
func (c *waitCloseConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
func (c *waitCloseConn) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *waitCloseConn) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func UpstreamMetadata(metadata M.Metadata) M.Metadata {
|
||||
return M.Metadata{
|
||||
Source: metadata.Source,
|
||||
@ -116,15 +84,20 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
|
||||
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()
|
||||
wg.Add(1)
|
||||
|
||||
if deadline.NeedAdditionalReadDeadline(conn) {
|
||||
conn = N.NewDeadlineConn(conn) // conn from sing should check NeedAdditionalReadDeadline
|
||||
}
|
||||
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{ExtendedConn: N.NewExtendedConn(conn), wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, combineAdditions(ctx, h.Additions)...)
|
||||
|
||||
cMetadata := &C.Metadata{
|
||||
NetWork: C.TCP,
|
||||
Type: h.Type,
|
||||
}
|
||||
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(metadata.Destination), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr()))
|
||||
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
|
||||
inbound.ApplyAdditions(cMetadata, h.Additions...)
|
||||
|
||||
h.Tunnel.HandleTCPConn(conn, cMetadata) // this goroutine must exit after conn unused
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -169,18 +142,23 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
||||
}
|
||||
return err
|
||||
}
|
||||
target := socks5.ParseAddr(dest.String())
|
||||
packet := &packet{
|
||||
cPacket := &packet{
|
||||
conn: &conn2,
|
||||
mutex: &mutex,
|
||||
rAddr: metadata.Source.UDPAddr(),
|
||||
lAddr: conn.LocalAddr(),
|
||||
buff: buff,
|
||||
}
|
||||
select {
|
||||
case h.UdpIn <- inbound.NewPacket(target, packet, h.Type, combineAdditions(ctx, h.Additions)...):
|
||||
default:
|
||||
|
||||
cMetadata := &C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
Type: h.Type,
|
||||
}
|
||||
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(dest), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr()))
|
||||
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
|
||||
inbound.ApplyAdditions(cMetadata, h.Additions...)
|
||||
|
||||
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -215,12 +193,6 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||
err = errors.New("address is invalid")
|
||||
return
|
||||
}
|
||||
buff := buf.NewPacket()
|
||||
defer buff.Release()
|
||||
n, err = buff.Write(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
@ -229,6 +201,14 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||
err = errors.New("writeBack to closed connection")
|
||||
return
|
||||
}
|
||||
|
||||
buff := buf.NewPacket()
|
||||
defer buff.Release()
|
||||
n, err = buff.Write(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = conn.WritePacket(buff, M.SocksaddrFromNet(addr))
|
||||
if err != nil {
|
||||
return
|
||||
@ -248,11 +228,3 @@ func (c *packet) Drop() {
|
||||
func (c *packet) InAddr() net.Addr {
|
||||
return c.lAddr
|
||||
}
|
||||
|
||||
func (c *packet) SetNatTable(natTable C.NatTable) {
|
||||
// no need
|
||||
}
|
||||
|
||||
func (c *packet) SetUdpInChan(in chan<- C.PacketAdapter) {
|
||||
// no need
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ type Listener struct {
|
||||
services []*hysteria2.Service[string]
|
||||
}
|
||||
|
||||
func New(config LC.Hysteria2Server, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (*Listener, error) {
|
||||
func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
var sl *Listener
|
||||
var err error
|
||||
if len(additions) == 0 {
|
||||
@ -43,15 +43,14 @@ func New(config LC.Hysteria2Server, tcpIn chan<- C.ConnContext, udpIn chan<- C.P
|
||||
}
|
||||
|
||||
h := &sing.ListenerHandler{
|
||||
TcpIn: tcpIn,
|
||||
UdpIn: udpIn,
|
||||
Tunnel: tunnel,
|
||||
Type: C.HYSTERIA2,
|
||||
Additions: additions,
|
||||
}
|
||||
|
||||
sl = &Listener{false, config, nil, nil}
|
||||
|
||||
cert, err := CN.ParseCert(config.Certificate, config.PrivateKey)
|
||||
cert, err := CN.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ type Listener struct {
|
||||
|
||||
var _listener *Listener
|
||||
|
||||
func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (C.MultiAddrListener, error) {
|
||||
func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addition) (C.MultiAddrListener, error) {
|
||||
var sl *Listener
|
||||
var err error
|
||||
if len(additions) == 0 {
|
||||
@ -51,8 +51,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C
|
||||
udpTimeout := int64(sing.UDPTimeout.Seconds())
|
||||
|
||||
h := &sing.ListenerHandler{
|
||||
TcpIn: tcpIn,
|
||||
UdpIn: udpIn,
|
||||
Tunnel: tunnel,
|
||||
Type: C.SHADOWSOCKS,
|
||||
Additions: additions,
|
||||
}
|
||||
@ -68,7 +67,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C
|
||||
sl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h, ntp.Now)
|
||||
default:
|
||||
err = fmt.Errorf("shadowsocks: unsupported method: %s", config.Cipher)
|
||||
return embedSS.New(config, tcpIn, udpIn)
|
||||
return embedSS.New(config, tunnel)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -148,7 +147,7 @@ func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C
|
||||
}
|
||||
N.TCPKeepAlive(c)
|
||||
|
||||
go sl.HandleConn(c, tcpIn)
|
||||
go sl.HandleConn(c, tunnel)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -188,7 +187,7 @@ func (l *Listener) AddrList() (addrList []net.Addr) {
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
||||
func (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
ctx := sing.WithAdditions(context.TODO(), additions...)
|
||||
err := l.service.NewConnection(ctx, conn, M.Metadata{
|
||||
Protocol: "shadowsocks",
|
||||
@ -200,10 +199,10 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions
|
||||
}
|
||||
}
|
||||
|
||||
func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool {
|
||||
func HandleShadowSocks(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) bool {
|
||||
if _listener != nil && _listener.service != nil {
|
||||
go _listener.HandleConn(conn, in, additions...)
|
||||
go _listener.HandleConn(conn, tunnel, additions...)
|
||||
return true
|
||||
}
|
||||
return embedSS.HandleShadowSocks(conn, in, additions...)
|
||||
return embedSS.HandleShadowSocks(conn, tunnel, additions...)
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ func checkTunName(tunName string) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (l *Listener, err error) {
|
||||
func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Listener, err error) {
|
||||
if len(additions) == 0 {
|
||||
additions = []inbound.Addition{
|
||||
inbound.WithInName("DEFAULT-TUN"),
|
||||
@ -142,18 +142,17 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
|
||||
dnsAdds = append(dnsAdds, addrPort)
|
||||
}
|
||||
for _, a := range options.Inet4Address {
|
||||
addrPort := netip.AddrPortFrom(a.Build().Addr().Next(), 53)
|
||||
addrPort := netip.AddrPortFrom(a.Addr().Next(), 53)
|
||||
dnsAdds = append(dnsAdds, addrPort)
|
||||
}
|
||||
for _, a := range options.Inet6Address {
|
||||
addrPort := netip.AddrPortFrom(a.Build().Addr().Next(), 53)
|
||||
addrPort := netip.AddrPortFrom(a.Addr().Next(), 53)
|
||||
dnsAdds = append(dnsAdds, addrPort)
|
||||
}
|
||||
|
||||
handler := &ListenerHandler{
|
||||
ListenerHandler: sing.ListenerHandler{
|
||||
TcpIn: tcpIn,
|
||||
UdpIn: udpIn,
|
||||
Tunnel: tunnel,
|
||||
Type: C.TUN,
|
||||
Additions: additions,
|
||||
UDPTimeout: time.Second * time.Duration(udpTimeout),
|
||||
@ -202,12 +201,12 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte
|
||||
tunOptions := tun.Options{
|
||||
Name: tunName,
|
||||
MTU: tunMTU,
|
||||
Inet4Address: common.Map(options.Inet4Address, LC.ListenPrefix.Build),
|
||||
Inet6Address: common.Map(options.Inet6Address, LC.ListenPrefix.Build),
|
||||
Inet4Address: options.Inet4Address,
|
||||
Inet6Address: options.Inet6Address,
|
||||
AutoRoute: options.AutoRoute,
|
||||
StrictRoute: options.StrictRoute,
|
||||
Inet4RouteAddress: common.Map(options.Inet4RouteAddress, LC.ListenPrefix.Build),
|
||||
Inet6RouteAddress: common.Map(options.Inet6RouteAddress, LC.ListenPrefix.Build),
|
||||
Inet4RouteAddress: options.Inet4RouteAddress,
|
||||
Inet6RouteAddress: options.Inet6RouteAddress,
|
||||
IncludeUID: includeUID,
|
||||
ExcludeUID: excludeUID,
|
||||
IncludeAndroidUser: options.IncludeAndroidUser,
|
||||
|
@ -2,7 +2,9 @@ package sing_vmess
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
@ -12,6 +14,7 @@ import (
|
||||
LC "github.com/Dreamacro/clash/listener/config"
|
||||
"github.com/Dreamacro/clash/listener/sing"
|
||||
"github.com/Dreamacro/clash/ntp"
|
||||
clashVMess "github.com/Dreamacro/clash/transport/vmess"
|
||||
|
||||
vmess "github.com/metacubex/sing-vmess"
|
||||
"github.com/sagernet/sing/common"
|
||||
@ -27,7 +30,7 @@ type Listener struct {
|
||||
|
||||
var _listener *Listener
|
||||
|
||||
func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (sl *Listener, err error) {
|
||||
func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) (sl *Listener, err error) {
|
||||
if len(additions) == 0 {
|
||||
additions = []inbound.Addition{
|
||||
inbound.WithInName("DEFAULT-VMESS"),
|
||||
@ -38,8 +41,7 @@ func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packe
|
||||
}()
|
||||
}
|
||||
h := &sing.ListenerHandler{
|
||||
TcpIn: tcpIn,
|
||||
UdpIn: udpIn,
|
||||
Tunnel: tunnel,
|
||||
Type: C.VMESS,
|
||||
Additions: additions,
|
||||
}
|
||||
@ -66,6 +68,29 @@ func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packe
|
||||
|
||||
sl = &Listener{false, config, nil, service}
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
var httpMux *http.ServeMux
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
if config.WsPath != "" {
|
||||
httpMux = http.NewServeMux()
|
||||
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := clashVMess.StreamUpgradedWebsocketConn(w, r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
sl.HandleConn(conn, tunnel)
|
||||
})
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
||||
}
|
||||
|
||||
for _, addr := range strings.Split(config.Listen, ",") {
|
||||
addr := addr
|
||||
|
||||
@ -74,9 +99,16 @@ func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packe
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(tlsConfig.Certificates) > 0 {
|
||||
l = tls.NewListener(l, tlsConfig)
|
||||
}
|
||||
sl.listeners = append(sl.listeners, l)
|
||||
|
||||
go func() {
|
||||
if httpMux != nil {
|
||||
_ = http.Serve(l, httpMux)
|
||||
return
|
||||
}
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
@ -87,7 +119,7 @@ func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packe
|
||||
}
|
||||
N.TCPKeepAlive(c)
|
||||
|
||||
go sl.HandleConn(c, tcpIn)
|
||||
go sl.HandleConn(c, tunnel)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -122,7 +154,7 @@ func (l *Listener) AddrList() (addrList []net.Addr) {
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
||||
func (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
ctx := sing.WithAdditions(context.TODO(), additions...)
|
||||
err := l.service.NewConnection(ctx, conn, metadata.Metadata{
|
||||
Protocol: "vmess",
|
||||
@ -134,9 +166,9 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions
|
||||
}
|
||||
}
|
||||
|
||||
func HandleVmess(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool {
|
||||
func HandleVmess(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) bool {
|
||||
if _listener != nil && _listener.service != nil {
|
||||
go _listener.HandleConn(conn, in, additions...)
|
||||
go _listener.HandleConn(conn, tunnel, additions...)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -34,7 +34,7 @@ func (l *Listener) Close() error {
|
||||
return l.listener.Close()
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) {
|
||||
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
if len(additions) == 0 {
|
||||
additions = []inbound.Addition{
|
||||
inbound.WithInName("DEFAULT-SOCKS"),
|
||||
@ -59,14 +59,14 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*
|
||||
}
|
||||
continue
|
||||
}
|
||||
go handleSocks(c, in, additions...)
|
||||
go handleSocks(c, tunnel, additions...)
|
||||
}
|
||||
}()
|
||||
|
||||
return sl, nil
|
||||
}
|
||||
|
||||
func handleSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
||||
func handleSocks(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
N.TCPKeepAlive(conn)
|
||||
bufConn := N.NewBufferedConn(conn)
|
||||
head, err := bufConn.Peek(1)
|
||||
@ -77,25 +77,33 @@ func handleSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Ad
|
||||
|
||||
switch head[0] {
|
||||
case socks4.Version:
|
||||
HandleSocks4(bufConn, in, additions...)
|
||||
HandleSocks4(bufConn, tunnel, additions...)
|
||||
case socks5.Version:
|
||||
HandleSocks5(bufConn, in, additions...)
|
||||
HandleSocks5(bufConn, tunnel, additions...)
|
||||
default:
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSocks4(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
||||
addr, _, err := socks4.ServerHandshake(conn, authStore.Authenticator())
|
||||
func HandleSocks4(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
authenticator := authStore.Authenticator()
|
||||
if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) {
|
||||
authenticator = nil
|
||||
}
|
||||
addr, _, err := socks4.ServerHandshake(conn, authenticator)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
in <- inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4, additions...)
|
||||
tunnel.HandleTCPConn(inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4, additions...))
|
||||
}
|
||||
|
||||
func HandleSocks5(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) {
|
||||
target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator())
|
||||
func HandleSocks5(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
authenticator := authStore.Authenticator()
|
||||
if inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) {
|
||||
authenticator = nil
|
||||
}
|
||||
target, command, err := socks5.ServerHandshake(conn, authenticator)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
@ -105,5 +113,5 @@ func HandleSocks5(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.A
|
||||
io.Copy(io.Discard, conn)
|
||||
return
|
||||
}
|
||||
in <- inbound.NewSocket(target, conn, C.SOCKS5, additions...)
|
||||
tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.SOCKS5, additions...))
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ func (l *UDPListener) Close() error {
|
||||
return l.packetConn.Close()
|
||||
}
|
||||
|
||||
func NewUDP(addr string, in chan<- C.PacketAdapter, additions ...inbound.Addition) (*UDPListener, error) {
|
||||
func NewUDP(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPListener, error) {
|
||||
if len(additions) == 0 {
|
||||
additions = []inbound.Addition{
|
||||
inbound.WithInName("DEFAULT-SOCKS"),
|
||||
@ -66,14 +66,14 @@ func NewUDP(addr string, in chan<- C.PacketAdapter, additions ...inbound.Additio
|
||||
}
|
||||
continue
|
||||
}
|
||||
handleSocksUDP(l, in, data, put, remoteAddr, additions...)
|
||||
handleSocksUDP(l, tunnel, data, put, remoteAddr, additions...)
|
||||
}
|
||||
}()
|
||||
|
||||
return sl, nil
|
||||
}
|
||||
|
||||
func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, put func(), addr net.Addr, additions ...inbound.Addition) {
|
||||
func handleSocksUDP(pc net.PacketConn, tunnel C.Tunnel, buf []byte, put func(), addr net.Addr, additions ...inbound.Addition) {
|
||||
target, payload, err := socks5.DecodeUDPPacket(buf)
|
||||
if err != nil {
|
||||
// Unresolved UDP packet, return buffer to the pool
|
||||
@ -88,8 +88,5 @@ func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, pu
|
||||
payload: payload,
|
||||
put: put,
|
||||
}
|
||||
select {
|
||||
case in <- inbound.NewPacket(target, packet, C.SOCKS5, additions...):
|
||||
default:
|
||||
}
|
||||
tunnel.HandleUDPPacket(inbound.NewPacket(target, packet, C.SOCKS5, additions...))
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user