mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 10:07:20 +10:00
Compare commits
15 Commits
v1.1-beta4
...
v1.1-beta5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d24cf5ec4 | ||
|
|
668d354771 | ||
|
|
ad14719b14 | ||
|
|
d9aa0a67d6 | ||
|
|
92bf784f4f | ||
|
|
395b13103a | ||
|
|
628cf56d3c | ||
|
|
ac5582537f | ||
|
|
9aa7a20d96 | ||
|
|
189f02c802 | ||
|
|
2373281c41 | ||
|
|
e8f4c2d36f | ||
|
|
07b6db23c1 | ||
|
|
9a3360e5d0 | ||
|
|
007a278ac8 |
34
.github/workflows/test.yml
vendored
Normal file
34
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Test build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
- dev-next
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Debug build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get latest go version
|
||||
id: version
|
||||
run: |
|
||||
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go-${{ hashFiles('**/go.sum') }}
|
||||
- name: Run Test
|
||||
run: make test
|
||||
@@ -9,8 +9,9 @@ builds:
|
||||
gcflags:
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Commit={{ .ShortCommit }} -s -w -buildid=
|
||||
- -s -w -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_wireguard
|
||||
- with_clash_api
|
||||
|
||||
@@ -8,9 +8,9 @@ ENV CGO_ENABLED=0
|
||||
RUN set -ex \
|
||||
&& apk add git build-base \
|
||||
&& export COMMIT=$(git rev-parse --short HEAD) \
|
||||
&& go build -v -trimpath -tags 'no_gvisor,with_quic,with_wireguard,with_acme' \
|
||||
&& go build -v -trimpath -tags with_quic,with_wireguard,with_acme \
|
||||
-o /go/bin/sing-box \
|
||||
-ldflags "-X github.com/sagernet/sing-box/constant.Commit=${COMMIT} -w -s -buildid=" \
|
||||
-ldflags "-s -w -buildid=" \
|
||||
./cmd/sing-box
|
||||
FROM alpine AS dist
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
|
||||
11
Makefile
11
Makefile
@@ -1,9 +1,7 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS ?= with_quic,with_wireguard,with_clash_api
|
||||
PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags \
|
||||
'-X "github.com/sagernet/sing-box/constant.Commit=$(COMMIT)" \
|
||||
-w -s -buildid='
|
||||
TAGS ?= with_gvisor,with_quic,with_wireguard,with_clash_api
|
||||
PARAMS = -v -trimpath -tags '$(TAGS)' -ldflags '-s -w -buildid='
|
||||
MAIN = ./cmd/sing-box
|
||||
|
||||
.PHONY: test release
|
||||
@@ -62,10 +60,9 @@ release_install:
|
||||
|
||||
test:
|
||||
@go test -v . && \
|
||||
pushd test && \
|
||||
cd test && \
|
||||
go mod tidy && \
|
||||
go test -v -tags with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr . && \
|
||||
popd
|
||||
go test -v -tags with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr .
|
||||
|
||||
clean:
|
||||
rm -rf bin dist
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/sagernet/sing/common/control"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
mdns "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type Router interface {
|
||||
@@ -27,11 +27,11 @@ type Router interface {
|
||||
GeoIPReader() *geoip.Reader
|
||||
LoadGeosite(code string) (Rule, error)
|
||||
|
||||
Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error)
|
||||
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
|
||||
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
|
||||
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
||||
|
||||
InterfaceBindManager() control.BindManager
|
||||
InterfaceFinder() control.InterfaceFinder
|
||||
DefaultInterface() string
|
||||
AutoDetectInterface() bool
|
||||
DefaultMark() int
|
||||
|
||||
@@ -3,9 +3,9 @@ package main
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -25,30 +25,40 @@ func init() {
|
||||
}
|
||||
|
||||
func printVersion(cmd *cobra.Command, args []string) {
|
||||
var version string
|
||||
if !nameOnly {
|
||||
version = "sing-box "
|
||||
if nameOnly {
|
||||
os.Stdout.WriteString(C.Version + "\n")
|
||||
return
|
||||
}
|
||||
version += F.ToString(C.Version)
|
||||
if C.Commit != "" {
|
||||
version += "." + C.Commit
|
||||
}
|
||||
if !nameOnly {
|
||||
version += " ("
|
||||
version += runtime.Version()
|
||||
version += ", "
|
||||
version += runtime.GOOS
|
||||
version += "/"
|
||||
version += runtime.GOARCH
|
||||
version += ", "
|
||||
version += "CGO "
|
||||
if C.CGO_ENABLED {
|
||||
version += "enabled"
|
||||
} else {
|
||||
version += "disabled"
|
||||
version := "sing-box version " + C.Version + "\n\n"
|
||||
version += "Environment: " + runtime.Version() + " " + runtime.GOOS + "/" + runtime.GOARCH + "\n"
|
||||
|
||||
var tags string
|
||||
var revision string
|
||||
|
||||
debugInfo, loaded := debug.ReadBuildInfo()
|
||||
if loaded {
|
||||
for _, setting := range debugInfo.Settings {
|
||||
switch setting.Key {
|
||||
case "-tags":
|
||||
tags = setting.Value
|
||||
case "vcs.revision":
|
||||
revision = setting.Value
|
||||
}
|
||||
}
|
||||
version += ")"
|
||||
}
|
||||
version += "\n"
|
||||
|
||||
if tags != "" {
|
||||
version += "Tags: " + tags + "\n"
|
||||
}
|
||||
if revision != "" {
|
||||
version += "Revision: " + revision + "\n"
|
||||
}
|
||||
|
||||
if C.CGO_ENABLED {
|
||||
version += "CGO: enabled\n"
|
||||
} else {
|
||||
version += "CGO: disabled\n"
|
||||
}
|
||||
|
||||
os.Stdout.WriteString(version)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
_ "github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -65,25 +65,23 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
var listener net.ListenConfig
|
||||
if options.BindInterface != "" {
|
||||
warnBindInterfaceOnUnsupportedPlatform.Check()
|
||||
bindFunc := control.BindToInterface(router.InterfaceBindManager(), options.BindInterface)
|
||||
bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1)
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else if router.AutoDetectInterface() {
|
||||
if C.IsWindows {
|
||||
bindFunc := control.BindToInterfaceIndexFunc(func(network, address string) int {
|
||||
return router.InterfaceMonitor().DefaultInterfaceIndex(M.ParseSocksaddr(address).Addr)
|
||||
})
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else {
|
||||
bindFunc := control.BindToInterfaceFunc(router.InterfaceBindManager(), func(network, address string) string {
|
||||
return router.InterfaceMonitor().DefaultInterfaceName(M.ParseSocksaddr(address).Addr)
|
||||
})
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
}
|
||||
const useInterfaceName = C.IsLinux
|
||||
bindFunc := control.BindToInterfaceFunc(router.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||||
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||
if C.IsLinux {
|
||||
return router.InterfaceMonitor().DefaultInterfaceName(remoteAddr), -1
|
||||
} else {
|
||||
return "", router.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
|
||||
}
|
||||
})
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else if router.DefaultInterface() != "" {
|
||||
bindFunc := control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface())
|
||||
bindFunc := control.BindToInterface(router.InterfaceFinder(), router.DefaultInterface(), -1)
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.InboundContext, error) {
|
||||
@@ -44,18 +44,10 @@ func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.
|
||||
}
|
||||
|
||||
func DomainNameQuery(ctx context.Context, packet []byte) (*adapter.InboundContext, error) {
|
||||
var parser dnsmessage.Parser
|
||||
_, err := parser.Start(packet)
|
||||
var msg mDNS.Msg
|
||||
err := msg.Unpack(packet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
question, err := parser.Question()
|
||||
if err != nil {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
domain := question.Name.String()
|
||||
if question.Class == dnsmessage.ClassINET && IsDomainName(domain) {
|
||||
return &adapter.InboundContext{Protocol: C.ProtocolDNS /*, Domain: domain*/}, nil
|
||||
}
|
||||
return nil, os.ErrInvalid
|
||||
return &adapter.InboundContext{Protocol: C.ProtocolDNS}, nil
|
||||
}
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"crypto/tls"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/certmagic"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/mholt/acmez/acme"
|
||||
)
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
type echClientConfig struct {
|
||||
@@ -170,15 +169,15 @@ const typeHTTPS = 65
|
||||
|
||||
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
||||
return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
||||
message := &dnsmessage.Message{
|
||||
Header: dnsmessage.Header{
|
||||
message := &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
RecursionDesired: true,
|
||||
},
|
||||
Questions: []dnsmessage.Question{
|
||||
Question: []mDNS.Question{
|
||||
{
|
||||
Name: dnsmessage.MustNewName(serverName + "."),
|
||||
Type: typeHTTPS,
|
||||
Class: dnsmessage.ClassINET,
|
||||
Name: serverName + ".",
|
||||
Qtype: mDNS.TypeHTTPS,
|
||||
Qclass: mDNS.ClassINET,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -186,19 +185,10 @@ func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serve
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.RCode != dnsmessage.RCodeSuccess {
|
||||
return nil, dns.RCodeError(response.RCode)
|
||||
if response.Rcode != mDNS.RcodeSuccess {
|
||||
return nil, dns.RCodeError(response.Rcode)
|
||||
}
|
||||
content, err := response.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mMsg mDNS.Msg
|
||||
err = mMsg.Unpack(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rr := range mMsg.Answer {
|
||||
for _, rr := range response.Answer {
|
||||
switch resource := rr.(type) {
|
||||
case *mDNS.HTTPS:
|
||||
for _, value := range resource.Value {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
//go:build with_quic
|
||||
|
||||
package constant
|
||||
|
||||
const QUIC_AVAILABLE = true
|
||||
@@ -1,9 +0,0 @@
|
||||
//go:build !with_quic
|
||||
|
||||
package constant
|
||||
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
const QUIC_AVAILABLE = false
|
||||
|
||||
var ErrQUICNotIncluded = E.New(`QUIC is not included in this build, rebuild with -tags with_quic`)
|
||||
@@ -1,6 +1,3 @@
|
||||
package constant
|
||||
|
||||
var (
|
||||
Version = "1.1-beta4"
|
||||
Commit = ""
|
||||
)
|
||||
var Version = "1.1-beta5"
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
#### 1.1-beta5
|
||||
|
||||
* Print tags in version command
|
||||
* Redirect clash hello to external ui
|
||||
* Move shadowsocksr implementation to clash
|
||||
* Make gVisor optional **1**
|
||||
* Refactor to miekg/dns
|
||||
* Refactor bind control
|
||||
* Fix build on go1.18
|
||||
* Fix clash store-selected
|
||||
* Fix close grpc conn
|
||||
* Fix port rule match logic
|
||||
* Fix clash api proxy type
|
||||
|
||||
*1*:
|
||||
|
||||
The build tag `no_gvisor` is replaced by `with_gvisor`.
|
||||
|
||||
The default tun stack is changed to system.
|
||||
|
||||
#### 1.0.4
|
||||
|
||||
* Fix close grpc conn
|
||||
* Fix port rule match logic
|
||||
* Fix clash api proxy type
|
||||
|
||||
#### 1.1-beta4
|
||||
|
||||
* Add internal simple-obfs and v2ray-plugin [Shadowsocks plugins](/configuration/outbound/shadowsocks#plugin)
|
||||
|
||||
@@ -104,8 +104,10 @@
|
||||
|
||||
The default rule uses the following matching logic:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
`other fields`
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
|
||||
@@ -103,8 +103,10 @@
|
||||
|
||||
默认规则使用以下匹配逻辑:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
`other fields`
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"auto_route": true,
|
||||
"strict_route": true,
|
||||
"endpoint_independent_nat": false,
|
||||
"stack": "gvisor",
|
||||
"stack": "system",
|
||||
"include_uid": [
|
||||
0
|
||||
],
|
||||
@@ -112,15 +112,15 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
|
||||
|
||||
TCP/IP stack.
|
||||
|
||||
| Stack | Description | Status |
|
||||
|------------------|--------------------------------------------------------------------------------|-------------------|
|
||||
| gVisor (default) | Based on [google/gvisor](https://github.com/google/gvisor) | recommended |
|
||||
| system | Less compatibility and sometimes better performance. | recommended |
|
||||
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
|
||||
| Stack | Description | Status |
|
||||
|------------------|----------------------------------------------------------------------------------|-------------------|
|
||||
| system (default) | Sometimes better performance | recommended |
|
||||
| gVisor | Better compatibility, based on [google/gvisor](https://github.com/google/gvisor) | recommended |
|
||||
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
The LWIP stack is not included by default, see [Installation](/#installation).
|
||||
gVisor and LWIP stacks is not included by default, see [Installation](/#installation).
|
||||
|
||||
#### include_uid
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"auto_route": true,
|
||||
"strict_route": true,
|
||||
"endpoint_independent_nat": false,
|
||||
"stack": "gvisor",
|
||||
"stack": "system",
|
||||
"include_uid": [
|
||||
0
|
||||
],
|
||||
@@ -107,15 +107,15 @@ UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。
|
||||
|
||||
TCP/IP 栈。
|
||||
|
||||
| 栈 | 描述 | 状态 |
|
||||
|------------------|--------------------------------------------------------------------------|-------|
|
||||
| gVisor (default) | 基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
|
||||
| system | 兼容性较差,有时性能更好。 | 推荐 |
|
||||
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
|
||||
| 栈 | 描述 | 状态 |
|
||||
|-------------|--------------------------------------------------------------------------|-------|
|
||||
| system (默认) | 有时性能更好 | 推荐 |
|
||||
| gVisor | 兼容性较好,基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
|
||||
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 LWIP 栈,请参阅 [安装](/zh/#_2)。
|
||||
默认安装不包含 gVisor 和 LWIP 栈,请参阅 [安装](/zh/#_2)。
|
||||
|
||||
#### include_uid
|
||||
|
||||
@@ -145,10 +145,10 @@ TCP/IP 栈。
|
||||
|
||||
限制被路由的 Android 用户。
|
||||
|
||||
| 常用用户 | ID |
|
||||
| 常用用户 | ID |
|
||||
|--|-----|
|
||||
| 您 | 0 |
|
||||
| 工作资料 | 10 |
|
||||
| 您 | 0 |
|
||||
| 工作资料 | 10 |
|
||||
|
||||
#### include_package
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
|
||||
WireGuard is not included by default, see [Installation](/#installation).
|
||||
|
||||
!!! warning ""
|
||||
|
||||
gVisor, which is required by the unprivileged WireGuard is not included by default, see [Installation](/#installation).
|
||||
|
||||
### Fields
|
||||
|
||||
#### server
|
||||
@@ -44,7 +48,9 @@ The server port.
|
||||
|
||||
Use system tun support.
|
||||
|
||||
Requires privileges and cannot conflict with system interfaces.
|
||||
Requires privilege and cannot conflict with system interfaces.
|
||||
|
||||
Forced if gVisor not included in the build.
|
||||
|
||||
#### interface_name
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
|
||||
默认安装不包含 WireGuard, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含被非特权 WireGuard 需要的 gVisor, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
### 字段
|
||||
|
||||
#### server
|
||||
@@ -46,6 +50,8 @@
|
||||
|
||||
需要特权且不能与系统接口冲突。
|
||||
|
||||
如果 gVisor 未包含在构建中,则强制执行。
|
||||
|
||||
#### interface_name
|
||||
|
||||
启用 `system_interface` 时的自定义设备名称。
|
||||
|
||||
@@ -107,8 +107,10 @@
|
||||
|
||||
The default rule uses the following matching logic:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
`other fields`
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
|
||||
@@ -105,8 +105,10 @@
|
||||
|
||||
默认规则使用以下匹配逻辑:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
`other fields`
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
|
||||
@@ -53,17 +53,6 @@ we need to do this.
|
||||
|
||||
The library needs to be updated with the upstream.
|
||||
|
||||
#### certmagic
|
||||
|
||||
Link: [GitHub repository](https://github.com/SagerNet/certmagic)
|
||||
|
||||
Fork of `caddyserver/certmagic`
|
||||
|
||||
Since upstream uses `miekg/dns` and we use `x/net/dnsmessage`, we need to replace its DNS part with our own
|
||||
implementation.
|
||||
|
||||
The library needs to be updated with the upstream.
|
||||
|
||||
#### smux
|
||||
|
||||
Link: [GitHub repository](https://github.com/SagerNet/smux)
|
||||
|
||||
@@ -32,7 +32,7 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
||||
| `with_utls` | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](./configuration/shared/tls#utls). |
|
||||
| `with_acme` | Build with ACME TLS certificate issuer support, see [TLS](./configuration/shared/tls). |
|
||||
| `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
|
||||
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||
| `with_gvisor` | Build with gVisor support, see [Tun inbound](./configuration/inbound/tun#stack) and [WireGuard outbound](./configuration/outbound/wireguard#system_interface). |
|
||||
| `with_embedded_tor` (CGO required) | Build with embedded Tor support, see [Tor outbound](./configuration/outbound/tor). |
|
||||
| `with_lwip` (CGO required) | Build with LWIP Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||
|
||||
|
||||
@@ -25,14 +25,14 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
||||
| 构建标志 | 描述 |
|
||||
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server),[Naive 入站](./configuration/inbound/naive),[Hysteria 入站](./configuration/inbound/hysteria),[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
|
||||
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
|
||||
| `with_grpc` | 启用标准 gRPCuTLS](https://github.com/refraction-networking/utls) 支持, 参阅 [TLS](./configuration/shared/tls#utls)。 |
|
||||
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||
| `with_clash_api` | 启用 Clash api 支 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
|
||||
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
||||
| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](./configuration/outbound/shadowsocksr)。 |
|
||||
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
|
||||
| `with_utls` | 启用 [uTLS](https://github.com/refraction-networking/utls) 支持, 参阅 [TLS](./configuration/shared/tls#utls)。 |
|
||||
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
| `with_utls` | 启用 [持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `with_gvisor` | 启用 gVisor 支持,参阅 [Tun 入站](./configuration/inbound/tun#stack) 和 [WireGuard 出站](./configuration/outbound/wireguard#system_interface)。 |
|
||||
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
|
||||
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
|
||||
|
||||
@@ -70,18 +70,30 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
||||
case C.TypeSocks:
|
||||
clashType = "Socks"
|
||||
case C.TypeHTTP:
|
||||
clashType = "Http"
|
||||
clashType = "HTTP"
|
||||
case C.TypeShadowsocks:
|
||||
clashType = "Shadowsocks"
|
||||
case C.TypeVMess:
|
||||
clashType = "Vmess"
|
||||
clashType = "VMess"
|
||||
case C.TypeTrojan:
|
||||
clashType = "Trojan"
|
||||
case C.TypeHysteria:
|
||||
clashType = "Hysteria"
|
||||
case C.TypeWireGuard:
|
||||
clashType = "WireGuard"
|
||||
case C.TypeShadowsocksR:
|
||||
clashType = "ShadowsocksR"
|
||||
case C.TypeVLESS:
|
||||
clashType = "VLESS"
|
||||
case C.TypeTor:
|
||||
clashType = "Tor"
|
||||
case C.TypeSSH:
|
||||
clashType = "SSH"
|
||||
case C.TypeSelector:
|
||||
clashType = "Selector"
|
||||
isGroup = true
|
||||
default:
|
||||
clashType = "Socks"
|
||||
clashType = "Direct"
|
||||
}
|
||||
info.Put("type", clashType)
|
||||
info.Put("name", detour.Tag())
|
||||
|
||||
@@ -61,6 +61,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
server.mode = "rule"
|
||||
}
|
||||
if options.StoreSelected {
|
||||
server.storeSelected = true
|
||||
cachePath := os.ExpandEnv(options.CacheFile)
|
||||
if cachePath == "" {
|
||||
cachePath = "cache.db"
|
||||
@@ -80,7 +81,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
chiRouter.Use(cors.Handler)
|
||||
chiRouter.Group(func(r chi.Router) {
|
||||
r.Use(authentication(options.Secret))
|
||||
r.Get("/", hello)
|
||||
r.Get("/", hello(options.ExternalUI != ""))
|
||||
r.Get("/logs", getLogs(logFactory))
|
||||
r.Get("/traffic", traffic(trafficManager))
|
||||
r.Get("/version", version)
|
||||
@@ -232,8 +233,14 @@ func authentication(serverSecret string) func(next http.Handler) http.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
func hello(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, render.M{"hello": "clash"})
|
||||
func hello(redirect bool) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if redirect {
|
||||
http.Redirect(w, r, "/ui/", http.StatusTemporaryRedirect)
|
||||
} else {
|
||||
render.JSON(w, r, render.M{"hello": "clash"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
|
||||
14
go.mod
14
go.mod
@@ -4,6 +4,8 @@ go 1.18
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/Dreamacro/clash v1.11.8
|
||||
github.com/caddyserver/certmagic v0.17.1
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/database64128/tfo-go v1.1.2
|
||||
@@ -20,13 +22,11 @@ require (
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/pires/go-proxyproto v0.6.2
|
||||
github.com/refraction-networking/utls v1.1.2
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0
|
||||
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
|
||||
github.com/sagernet/sing v0.0.0-20220915031330-38f39bc0c690
|
||||
github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24
|
||||
github.com/sagernet/sing-tun v0.0.0-20220915032336-60b1da576469
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
||||
@@ -37,8 +37,8 @@ require (
|
||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0
|
||||
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
|
||||
google.golang.org/grpc v1.49.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||
|
||||
35
go.sum
35
go.sum
@@ -3,6 +3,8 @@ berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+f
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Dreamacro/clash v1.11.8 h1:t/sy3/tiihRlvV3SsliYFjj8rKpbLw5IJ2PymiHcwS8=
|
||||
github.com/Dreamacro/clash v1.11.8/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
@@ -10,6 +12,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/caddyserver/certmagic v0.17.1 h1:VrWANhQAj3brK7jAUKyN6XBHg56WsyorI/84Ilq1tCQ=
|
||||
github.com/caddyserver/certmagic v0.17.1/go.mod h1:pSS2aZcdKlrTZrb2DKuRafckx20o5Fz1EdDKEB8KOQM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc h1:307gdRLiZ08dwOIKwc5lAQ19DRFaQQvdhHalyB4Asx8=
|
||||
@@ -91,8 +95,8 @@ github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
@@ -133,26 +137,22 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kxbgGa2Xp0H2CCsx1o2pVTt0f+hmfuHH4=
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 h1:lQ4RFWY/wBYmXl/zJJCwQbhiEIbgEqC7j+nhZYkgwmU=
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0/go.mod h1:xSHLCsdgy5QIozXFEv6uDgMWzyrRdFFjr3TgL+juu6g=
|
||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921 h1:xUHzlIbdlPV/fkToIO9futp9lmKIY+72ezk/whQ8XsI=
|
||||
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
|
||||
github.com/sagernet/sing v0.0.0-20220915031330-38f39bc0c690 h1:pvaLdkDmsGN2K46vf8rorAhYGFvKPuQNzcofuy3aXXg=
|
||||
github.com/sagernet/sing v0.0.0-20220915031330-38f39bc0c690/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8 h1:Iyfl+Rm5jcDvXuy/jpOBI3eu35ujci50tkqYHHwwg+8=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8/go.mod h1:bPVnJ5gJ0WmUfN1bJP9Cis0ab8SSByx6JVzyLJjDMwA=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24 h1:LsmPeFvj4GhiV5Y7Rm8I845XysdxVN4MQmfZ36P5bmw=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220915032336-60b1da576469 h1:tvGUJsOqxZ3ofAY9undQfQ+JCWvmIwLpIOC+XaBFO88=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220915032336-60b1da576469/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
||||
@@ -194,7 +194,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||
@@ -239,8 +238,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -248,7 +247,6 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -266,9 +264,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc h1:dpclq5m2YrqPGStKmtw7IcNbKLfbIqKXvNxDJKdIKYc=
|
||||
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -300,8 +297,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
I "github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (adapter.Inbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
return nil, I.ErrQUICNotIncluded
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
@@ -105,7 +106,7 @@ func (n *Naive) Start() error {
|
||||
|
||||
if common.Contains(n.network, N.NetworkUDP) {
|
||||
err := n.configureHTTP3Listener()
|
||||
if !C.QUIC_AVAILABLE && len(n.network) > 1 {
|
||||
if !include.WithQUIC && len(n.network) > 1 {
|
||||
log.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
} else if err != nil {
|
||||
return err
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
I "github.com/sagernet/sing-box/include"
|
||||
)
|
||||
|
||||
func (n *Naive) configureHTTP3Listener() error {
|
||||
return C.ErrQUICNotIncluded
|
||||
return I.ErrQUICNotIncluded
|
||||
}
|
||||
|
||||
7
include/quic.go
Normal file
7
include/quic.go
Normal file
@@ -0,0 +1,7 @@
|
||||
//go:build with_quic
|
||||
|
||||
package include
|
||||
|
||||
import _ "github.com/sagernet/sing-dns/quic"
|
||||
|
||||
const WithQUIC = true
|
||||
21
include/quic_stub.go
Normal file
21
include/quic_stub.go
Normal file
@@ -0,0 +1,21 @@
|
||||
//go:build !with_quic
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
const WithQUIC = false
|
||||
|
||||
var ErrQUICNotIncluded = E.New(`QUIC is not included in this build, rebuild with -tags with_quic`)
|
||||
|
||||
func init() {
|
||||
dns.RegisterTransport([]string{"quic", "h3"}, func(ctx context.Context, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
return nil, ErrQUICNotIncluded
|
||||
})
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*DNS)(nil)
|
||||
@@ -72,7 +72,7 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var message dnsmessage.Message
|
||||
var message mDNS.Msg
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -88,7 +88,7 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
|
||||
responseBuffer := common.Dup(_responseBuffer)
|
||||
defer responseBuffer.Release()
|
||||
responseBuffer.Resize(2, 0)
|
||||
n, err := response.AppendPack(responseBuffer.Index(0))
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var message dnsmessage.Message
|
||||
var message mDNS.Msg
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -131,7 +131,7 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
||||
}
|
||||
timeout.Update()
|
||||
responseBuffer := buf.NewPacket()
|
||||
n, err := response.AppendPack(responseBuffer.Index(0))
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
responseBuffer.Release()
|
||||
return err
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
I "github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (adapter.Outbound, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
return nil, I.ErrQUICNotIncluded
|
||||
}
|
||||
|
||||
@@ -4,41 +4,36 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/shadowsocksr"
|
||||
"github.com/sagernet/shadowsocksr/obfs"
|
||||
"github.com/sagernet/shadowsocksr/protocol"
|
||||
"github.com/sagernet/shadowsocksr/ssr"
|
||||
"github.com/sagernet/shadowsocksr/streamCipher"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowimpl"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing-box/transport/clashssr/obfs"
|
||||
"github.com/sagernet/sing-box/transport/clashssr/protocol"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
||||
"github.com/Dreamacro/clash/transport/shadowsocks/shadowstream"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*ShadowsocksR)(nil)
|
||||
|
||||
type ShadowsocksR struct {
|
||||
myOutboundAdapter
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
method shadowsocks.Method
|
||||
cipher string
|
||||
password string
|
||||
obfs string
|
||||
obfsParams *ssr.ServerInfo
|
||||
protocol string
|
||||
protocolParams *ssr.ServerInfo
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
cipher core.Cipher
|
||||
obfs obfs.Obfs
|
||||
protocol protocol.Protocol
|
||||
}
|
||||
|
||||
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (*ShadowsocksR, error) {
|
||||
@@ -52,40 +47,54 @@ func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.Cont
|
||||
},
|
||||
dialer: dialer.New(router, options.DialerOptions),
|
||||
serverAddr: options.ServerOptions.Build(),
|
||||
cipher: options.Method,
|
||||
password: options.Password,
|
||||
obfs: options.Obfs,
|
||||
protocol: options.Protocol,
|
||||
}
|
||||
var cipher string
|
||||
var err error
|
||||
outbound.method, err = shadowimpl.FetchMethod(options.Method, options.Password)
|
||||
switch options.Method {
|
||||
case "none":
|
||||
cipher = "dummy"
|
||||
default:
|
||||
cipher = options.Method
|
||||
}
|
||||
outbound.cipher, err = core.PickCipher(cipher, nil, options.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = streamCipher.NewStreamCipher(options.Method, options.Password); err != nil {
|
||||
return nil, E.New(strings.ToLower(err.Error()))
|
||||
var (
|
||||
ivSize int
|
||||
key []byte
|
||||
)
|
||||
if cipher == "dummy" {
|
||||
ivSize = 0
|
||||
key = core.Kdf(options.Password, 16)
|
||||
} else {
|
||||
streamCipher, ok := outbound.cipher.(*core.StreamCipher)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s is not none or a supported stream cipher in ssr", cipher)
|
||||
}
|
||||
ivSize = streamCipher.IVSize()
|
||||
key = streamCipher.Key
|
||||
}
|
||||
if obfs.NewObfs(options.Obfs) == nil {
|
||||
return nil, E.New("unknown obfs: " + options.Obfs)
|
||||
}
|
||||
outbound.obfsParams = &ssr.ServerInfo{
|
||||
Host: outbound.serverAddr.AddrString(),
|
||||
Port: outbound.serverAddr.Port,
|
||||
TcpMss: 1460,
|
||||
obfs, obfsOverhead, err := obfs.PickObfs(options.Obfs, &obfs.Base{
|
||||
Host: options.Server,
|
||||
Port: int(options.ServerPort),
|
||||
Key: key,
|
||||
IVSize: ivSize,
|
||||
Param: options.ObfsParam,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize obfs")
|
||||
}
|
||||
if protocol.NewProtocol(options.Protocol) == nil {
|
||||
return nil, E.New("unknown protocol: " + options.Protocol)
|
||||
}
|
||||
outbound.protocolParams = &ssr.ServerInfo{
|
||||
Host: outbound.serverAddr.AddrString(),
|
||||
Port: outbound.serverAddr.Port,
|
||||
TcpMss: 1460,
|
||||
Param: options.Protocol,
|
||||
}
|
||||
if outbound.method == nil {
|
||||
outbound.network = common.Filter(outbound.network, func(it string) bool { return it == N.NetworkTCP })
|
||||
protocol, err := protocol.PickProtocol(options.Protocol, &protocol.Base{
|
||||
Key: key,
|
||||
Overhead: obfsOverhead,
|
||||
Param: options.ProtocolParam,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize protocol")
|
||||
}
|
||||
outbound.obfs = obfs
|
||||
outbound.protocol = protocol
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
@@ -97,20 +106,17 @@ func (h *ShadowsocksR) DialContext(ctx context.Context, network string, destinat
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cipher, err := streamCipher.NewStreamCipher(h.cipher, h.password)
|
||||
conn = h.cipher.StreamConn(h.obfs.StreamConn(conn))
|
||||
writeIv, err := conn.(*shadowstream.Conn).ObtainWriteIV()
|
||||
if err != nil {
|
||||
return nil, E.New(strings.ToLower(err.Error()))
|
||||
return nil, err
|
||||
}
|
||||
ssConn := shadowsocksr.NewSSTCPConn(conn, cipher)
|
||||
ssConn.IObfs = obfs.NewObfs(h.obfs)
|
||||
ssConn.IObfs.SetServerInfo(h.obfsParams)
|
||||
ssConn.IProtocol = protocol.NewProtocol(h.protocol)
|
||||
ssConn.IProtocol.SetServerInfo(h.protocolParams)
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(ssConn, destination)
|
||||
conn = h.protocol.StreamConn(conn, writeIv)
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(conn, destination)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "write request")
|
||||
}
|
||||
return ssConn, nil
|
||||
return conn, nil
|
||||
case N.NetworkUDP:
|
||||
conn, err := h.ListenPacket(ctx, destination)
|
||||
if err != nil {
|
||||
@@ -128,7 +134,10 @@ func (h *ShadowsocksR) ListenPacket(ctx context.Context, destination M.Socksaddr
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.method.DialPacketConn(outConn), nil
|
||||
packetConn := h.cipher.PacketConn(bufio.NewUnbindPacketConn(outConn))
|
||||
packetConn = h.protocol.PacketConn(packetConn)
|
||||
packetConn = &ssPacketConn{packetConn, outConn.RemoteAddr()}
|
||||
return packetConn, nil
|
||||
}
|
||||
|
||||
func (h *ShadowsocksR) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
@@ -138,3 +147,36 @@ func (h *ShadowsocksR) NewConnection(ctx context.Context, conn net.Conn, metadat
|
||||
func (h *ShadowsocksR) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
type ssPacketConn struct {
|
||||
net.PacketConn
|
||||
rAddr net.Addr
|
||||
}
|
||||
|
||||
func (spc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return spc.PacketConn.WriteTo(packet[3:], spc.rAddr)
|
||||
}
|
||||
|
||||
func (spc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
n, _, e := spc.PacketConn.ReadFrom(b)
|
||||
if e != nil {
|
||||
return 0, nil, e
|
||||
}
|
||||
|
||||
addr := socks5.SplitAddr(b[:n])
|
||||
if addr == nil {
|
||||
return 0, nil, errors.New("parse addr error")
|
||||
}
|
||||
|
||||
udpAddr := addr.UDPAddr()
|
||||
if udpAddr == nil {
|
||||
return 0, nil, errors.New("parse addr error")
|
||||
}
|
||||
|
||||
copy(b, b[len(addr):])
|
||||
return n - len(addr), udpAddr, e
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/wireguard"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/debug"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -98,7 +99,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
}
|
||||
var wireTunDevice wireguard.Device
|
||||
var err error
|
||||
if !options.SystemInterface {
|
||||
if !options.SystemInterface && tun.WithGVisor {
|
||||
wireTunDevice, err = wireguard.NewStackDevice(localPrefixes, mtu)
|
||||
} else {
|
||||
wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, localPrefixes, mtu)
|
||||
|
||||
@@ -13,7 +13,7 @@ pushd $PROJECT
|
||||
git fetch
|
||||
git reset FETCH_HEAD --hard
|
||||
git clean -fdx
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags no_gvisor,with_quic,with_acme,debug ./cmd/sing-box
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_acme,debug ./cmd/sing-box
|
||||
popd
|
||||
|
||||
sudo systemctl stop sing-box
|
||||
|
||||
@@ -10,7 +10,7 @@ DIR=$(dirname "$0")
|
||||
PROJECT=$DIR/../..
|
||||
|
||||
pushd $PROJECT
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags no_gvisor,with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||
popd
|
||||
|
||||
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
curl -Lo go.tar.gz https://go.dev/dl/go1.19.linux-amd64.tar.gz
|
||||
curl -Lo go.tar.gz https://go.dev/dl/go1.19.1.linux-amd64.tar.gz
|
||||
sudo rm -rf /usr/local/go
|
||||
sudo tar -C /usr/local -xzf go.tar.gz
|
||||
rm go.tar.gz
|
||||
@@ -10,7 +10,7 @@ DIR=$(dirname "$0")
|
||||
PROJECT=$DIR/../..
|
||||
|
||||
pushd $PROJECT
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags no_gvisor,with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||
go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||
popd
|
||||
|
||||
sudo systemctl stop sing-box
|
||||
|
||||
50
route/interface_finder.go
Normal file
50
route/interface_finder.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
)
|
||||
|
||||
var _ control.InterfaceFinder = (*myInterfaceFinder)(nil)
|
||||
|
||||
type myInterfaceFinder struct {
|
||||
ifs []net.Interface
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) update() error {
|
||||
ifs, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.ifs = ifs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex int, err error) {
|
||||
for _, netInterface := range f.ifs {
|
||||
if netInterface.Name == name {
|
||||
return netInterface.Index, nil
|
||||
}
|
||||
}
|
||||
netInterface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.update()
|
||||
return netInterface.Index, nil
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) InterfaceNameByIndex(index int) (interfaceName string, err error) {
|
||||
for _, netInterface := range f.ifs {
|
||||
if netInterface.Index == index {
|
||||
return netInterface.Name, nil
|
||||
}
|
||||
}
|
||||
netInterface, err := net.InterfaceByIndex(index)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.update()
|
||||
return netInterface.Name, nil
|
||||
}
|
||||
@@ -86,7 +86,7 @@ type Router struct {
|
||||
transports []dns.Transport
|
||||
transportMap map[string]dns.Transport
|
||||
transportDomainStrategy map[dns.Transport]dns.DomainStrategy
|
||||
interfaceBindManager control.BindManager
|
||||
interfaceFinder myInterfaceFinder
|
||||
autoDetectInterface bool
|
||||
defaultInterface string
|
||||
defaultMark int
|
||||
@@ -123,7 +123,6 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
defaultDetour: options.Final,
|
||||
dnsClient: dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire),
|
||||
defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
|
||||
interfaceBindManager: control.NewBindManager(),
|
||||
autoDetectInterface: options.AutoDetectInterface,
|
||||
defaultInterface: options.DefaultInterface,
|
||||
defaultMark: options.DefaultMark,
|
||||
@@ -196,7 +195,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
||||
}
|
||||
}
|
||||
transport, err := dns.NewTransport(ctx, detour, server.Address)
|
||||
transport, err := dns.CreateTransport(ctx, detour, server.Address)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
||||
}
|
||||
@@ -233,7 +232,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
}
|
||||
if defaultTransport == nil {
|
||||
if len(transports) == 0 {
|
||||
transports = append(transports, dns.NewLocalTransport())
|
||||
transports = append(transports, &dns.LocalTransport{})
|
||||
}
|
||||
defaultTransport = transports[0]
|
||||
}
|
||||
@@ -247,13 +246,11 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || C.IsAndroid && inbound.TunOptions.AutoRoute
|
||||
})
|
||||
|
||||
if router.interfaceBindManager != nil || needInterfaceMonitor {
|
||||
if needInterfaceMonitor {
|
||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||
if err == nil {
|
||||
router.networkMonitor = networkMonitor
|
||||
if router.interfaceBindManager != nil {
|
||||
networkMonitor.RegisterCallback(router.interfaceBindManager.Update)
|
||||
}
|
||||
networkMonitor.RegisterCallback(router.interfaceFinder.update)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,8 +711,8 @@ func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, de
|
||||
return nil, defaultOutbound
|
||||
}
|
||||
|
||||
func (r *Router) InterfaceBindManager() control.BindManager {
|
||||
return r.interfaceBindManager
|
||||
func (r *Router) InterfaceFinder() control.InterfaceFinder {
|
||||
return &r.interfaceFinder
|
||||
}
|
||||
|
||||
func (r *Router) AutoDetectInterface() bool {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func (r *Router) matchDNS(ctx context.Context) (context.Context, dns.Transport, dns.DomainStrategy) {
|
||||
@@ -40,29 +40,29 @@ func (r *Router) matchDNS(ctx context.Context) (context.Context, dns.Transport,
|
||||
return ctx, r.defaultTransport, r.defaultDomainStrategy
|
||||
}
|
||||
|
||||
func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) {
|
||||
if len(message.Questions) > 0 {
|
||||
r.dnsLogger.DebugContext(ctx, "exchange ", formatDNSQuestion(message.Questions[0]))
|
||||
func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if len(message.Question) > 0 {
|
||||
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
|
||||
}
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
if len(message.Questions) > 0 {
|
||||
switch message.Questions[0].Type {
|
||||
case dnsmessage.TypeA:
|
||||
if len(message.Question) > 0 {
|
||||
switch message.Question[0].Qtype {
|
||||
case mDNS.TypeA:
|
||||
metadata.IPVersion = 4
|
||||
case dnsmessage.TypeAAAA:
|
||||
case mDNS.TypeAAAA:
|
||||
metadata.IPVersion = 6
|
||||
}
|
||||
metadata.Domain = string(message.Questions[0].Name.Data[:message.Questions[0].Name.Length-1])
|
||||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||
}
|
||||
ctx, transport, strategy := r.matchDNS(ctx)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
|
||||
defer cancel()
|
||||
response, err := r.dnsClient.Exchange(ctx, transport, message, strategy)
|
||||
if err != nil && len(message.Questions) > 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", message.Questions[0].Name.String()))
|
||||
if err != nil && len(message.Question) > 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
||||
}
|
||||
if len(message.Questions) > 0 && response != nil {
|
||||
LogDNSAnswers(r.dnsLogger, ctx, message.Questions[0].Name.String(), response.Answers)
|
||||
if len(message.Question) > 0 && response != nil {
|
||||
LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer)
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
@@ -93,61 +93,26 @@ func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr
|
||||
return r.Lookup(ctx, domain, dns.DomainStrategyAsIS)
|
||||
}
|
||||
|
||||
func LogDNSAnswers(logger log.ContextLogger, ctx context.Context, domain string, answers []dnsmessage.Resource) {
|
||||
for _, rawAnswer := range answers {
|
||||
var content string
|
||||
switch answer := rawAnswer.Body.(type) {
|
||||
case *dnsmessage.AResource:
|
||||
content = netip.AddrFrom4(answer.A).String()
|
||||
case *dnsmessage.NSResource:
|
||||
content = answer.NS.String()
|
||||
case *dnsmessage.CNAMEResource:
|
||||
content = answer.CNAME.String()
|
||||
case *dnsmessage.SOAResource:
|
||||
content = answer.MBox.String()
|
||||
case *dnsmessage.PTRResource:
|
||||
content = answer.PTR.String()
|
||||
case *dnsmessage.MXResource:
|
||||
content = answer.MX.String()
|
||||
case *dnsmessage.TXTResource:
|
||||
content = strings.Join(answer.TXT, " ")
|
||||
case *dnsmessage.AAAAResource:
|
||||
content = netip.AddrFrom16(answer.AAAA).String()
|
||||
case *dnsmessage.SRVResource:
|
||||
content = answer.Target.String()
|
||||
case *dnsmessage.UnknownResource:
|
||||
content = answer.Type.String()
|
||||
default:
|
||||
continue
|
||||
}
|
||||
rType := formatDNSType(rawAnswer.Header.Type)
|
||||
if rType == "" {
|
||||
logger.InfoContext(ctx, "exchanged ", domain, " ", rType)
|
||||
} else {
|
||||
logger.InfoContext(ctx, "exchanged ", domain, " ", rType, " ", content)
|
||||
}
|
||||
func LogDNSAnswers(logger log.ContextLogger, ctx context.Context, domain string, answers []mDNS.RR) {
|
||||
for _, answer := range answers {
|
||||
logger.InfoContext(ctx, "exchanged ", domain, " ", mDNS.Type(answer.Header().Rrtype).String(), " ", formatQuestion(answer.String()))
|
||||
}
|
||||
}
|
||||
|
||||
func formatDNSQuestion(question dnsmessage.Question) string {
|
||||
var qType string
|
||||
qType = question.Type.String()
|
||||
if len(qType) > 4 {
|
||||
qType = qType[4:]
|
||||
func fqdnToDomain(fqdn string) string {
|
||||
if mDNS.IsFqdn(fqdn) {
|
||||
return fqdn[:len(fqdn)-1]
|
||||
}
|
||||
var qClass string
|
||||
qClass = question.Class.String()
|
||||
if len(qClass) > 5 {
|
||||
qClass = qClass[5:]
|
||||
}
|
||||
return string(question.Name.Data[:question.Name.Length-1]) + " " + qType + " " + qClass
|
||||
return fqdn
|
||||
}
|
||||
|
||||
func formatDNSType(qType dnsmessage.Type) string {
|
||||
qTypeName := qType.String()
|
||||
if len(qTypeName) > 4 {
|
||||
return qTypeName[4:]
|
||||
} else {
|
||||
return F.ToString("unknown (type ", qTypeName, ")")
|
||||
func formatQuestion(string string) string {
|
||||
if strings.HasPrefix(string, ";") {
|
||||
string = string[1:]
|
||||
}
|
||||
string = strings.ReplaceAll(string, "\t", " ")
|
||||
for strings.Contains(string, " ") {
|
||||
string = strings.ReplaceAll(string, " ", " ")
|
||||
}
|
||||
return string
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ var _ adapter.Rule = (*DefaultRule)(nil)
|
||||
type DefaultRule struct {
|
||||
items []RuleItem
|
||||
sourceAddressItems []RuleItem
|
||||
sourcePortItems []RuleItem
|
||||
destinationAddressItems []RuleItem
|
||||
destinationPortItems []RuleItem
|
||||
allItems []RuleItem
|
||||
invert bool
|
||||
outbound string
|
||||
@@ -143,7 +145,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
item := NewPortItem(true, options.SourcePort)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePortRange) > 0 {
|
||||
@@ -151,12 +153,12 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "source_port_range")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.Port) > 0 {
|
||||
item := NewPortItem(false, options.Port)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.PortRange) > 0 {
|
||||
@@ -164,7 +166,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "port_range")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.ProcessName) > 0 {
|
||||
@@ -256,6 +258,19 @@ func (r *DefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.sourcePortItems) > 0 {
|
||||
var sourcePortMatch bool
|
||||
for _, item := range r.sourcePortItems {
|
||||
if item.Match(metadata) {
|
||||
sourcePortMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !sourcePortMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationAddressItems) > 0 {
|
||||
var destinationAddressMatch bool
|
||||
for _, item := range r.destinationAddressItems {
|
||||
@@ -269,6 +284,19 @@ func (r *DefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationPortItems) > 0 {
|
||||
var destinationPortMatch bool
|
||||
for _, item := range r.destinationPortItems {
|
||||
if item.Match(metadata) {
|
||||
destinationPortMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !destinationPortMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
return !r.invert
|
||||
}
|
||||
|
||||
|
||||
@@ -39,12 +39,15 @@ func NewDNSRule(router adapter.Router, logger log.ContextLogger, options option.
|
||||
var _ adapter.DNSRule = (*DefaultDNSRule)(nil)
|
||||
|
||||
type DefaultDNSRule struct {
|
||||
items []RuleItem
|
||||
addressItems []RuleItem
|
||||
allItems []RuleItem
|
||||
invert bool
|
||||
outbound string
|
||||
disableCache bool
|
||||
items []RuleItem
|
||||
sourceAddressItems []RuleItem
|
||||
sourcePortItems []RuleItem
|
||||
destinationAddressItems []RuleItem
|
||||
destinationPortItems []RuleItem
|
||||
allItems []RuleItem
|
||||
invert bool
|
||||
outbound string
|
||||
disableCache bool
|
||||
}
|
||||
|
||||
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
|
||||
@@ -90,12 +93,12 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
}
|
||||
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
|
||||
item := NewDomainItem(options.Domain, options.DomainSuffix)
|
||||
rule.addressItems = append(rule.addressItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DomainKeyword) > 0 {
|
||||
item := NewDomainKeywordItem(options.DomainKeyword)
|
||||
rule.addressItems = append(rule.addressItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DomainRegex) > 0 {
|
||||
@@ -103,17 +106,17 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "domain_regex")
|
||||
}
|
||||
rule.addressItems = append(rule.addressItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.Geosite) > 0 {
|
||||
item := NewGeositeItem(router, logger, options.Geosite)
|
||||
rule.addressItems = append(rule.addressItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourceGeoIP) > 0 {
|
||||
item := NewGeoIPItem(router, logger, true, options.SourceGeoIP)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourceIPCIDR) > 0 {
|
||||
@@ -121,12 +124,12 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "source_ipcidr")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
item := NewPortItem(true, options.SourcePort)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePortRange) > 0 {
|
||||
@@ -134,12 +137,12 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "source_port_range")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.Port) > 0 {
|
||||
item := NewPortItem(false, options.Port)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.PortRange) > 0 {
|
||||
@@ -147,7 +150,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "port_range")
|
||||
}
|
||||
rule.items = append(rule.items, item)
|
||||
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.ProcessName) > 0 {
|
||||
@@ -230,18 +233,59 @@ func (r *DefaultDNSRule) Match(metadata *adapter.InboundContext) bool {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
if len(r.addressItems) > 0 {
|
||||
var addressMatch bool
|
||||
for _, item := range r.addressItems {
|
||||
|
||||
if len(r.sourceAddressItems) > 0 {
|
||||
var sourceAddressMatch bool
|
||||
for _, item := range r.sourceAddressItems {
|
||||
if item.Match(metadata) {
|
||||
addressMatch = true
|
||||
sourceAddressMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !addressMatch {
|
||||
if !sourceAddressMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.sourcePortItems) > 0 {
|
||||
var sourcePortMatch bool
|
||||
for _, item := range r.sourcePortItems {
|
||||
if item.Match(metadata) {
|
||||
sourcePortMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !sourcePortMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationAddressItems) > 0 {
|
||||
var destinationAddressMatch bool
|
||||
for _, item := range r.destinationAddressItems {
|
||||
if item.Match(metadata) {
|
||||
destinationAddressMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !destinationAddressMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationPortItems) > 0 {
|
||||
var destinationPortMatch bool
|
||||
for _, item := range r.destinationPortItems {
|
||||
if item.Match(metadata) {
|
||||
destinationPortMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !destinationPortMatch {
|
||||
return r.invert
|
||||
}
|
||||
}
|
||||
|
||||
return !r.invert
|
||||
}
|
||||
|
||||
|
||||
10
test/go.mod
10
test/go.mod
@@ -10,7 +10,7 @@ require (
|
||||
github.com/docker/docker v20.10.18+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/gofrs/uuid v4.3.0+incompatible
|
||||
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921
|
||||
github.com/sagernet/sing v0.0.0-20220914045234-93cc53b60cee
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
@@ -24,6 +24,7 @@ require (
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/caddyserver/certmagic v0.17.1 // indirect
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc // indirect
|
||||
github.com/cretz/bine v0.2.0 // indirect
|
||||
github.com/database64128/tfo-go v1.1.2 // indirect
|
||||
@@ -61,13 +62,12 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/refraction-networking/utls v1.1.2 // indirect
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a // indirect
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 // indirect
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 // indirect
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24 // indirect
|
||||
github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8 // indirect
|
||||
github.com/sagernet/sing-tun v0.0.0-20220914100102-057dd738a7f7 // indirect
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
|
||||
@@ -81,7 +81,7 @@ require (
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 // indirect
|
||||
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
|
||||
|
||||
20
test/go.sum
20
test/go.sum
@@ -14,6 +14,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/caddyserver/certmagic v0.17.1 h1:VrWANhQAj3brK7jAUKyN6XBHg56WsyorI/84Ilq1tCQ=
|
||||
github.com/caddyserver/certmagic v0.17.1/go.mod h1:pSS2aZcdKlrTZrb2DKuRafckx20o5Fz1EdDKEB8KOQM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc h1:307gdRLiZ08dwOIKwc5lAQ19DRFaQQvdhHalyB4Asx8=
|
||||
@@ -153,8 +155,6 @@ github.com/refraction-networking/utls v1.1.2/go.mod h1:+D89TUtA8+NKVFj1IXWr0p3tS
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kxbgGa2Xp0H2CCsx1o2pVTt0f+hmfuHH4=
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
@@ -165,14 +165,14 @@ github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 h1:lQ4RFWY/w
|
||||
github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0/go.mod h1:xSHLCsdgy5QIozXFEv6uDgMWzyrRdFFjr3TgL+juu6g=
|
||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921 h1:xUHzlIbdlPV/fkToIO9futp9lmKIY+72ezk/whQ8XsI=
|
||||
github.com/sagernet/sing v0.0.0-20220913004915-27ddefbb8921/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
|
||||
github.com/sagernet/sing v0.0.0-20220914045234-93cc53b60cee h1:+3w7+QWnhWi3Qz7+Xcais8zViHRUPIkmxq3eYZm/zvk=
|
||||
github.com/sagernet/sing v0.0.0-20220914045234-93cc53b60cee/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8 h1:Iyfl+Rm5jcDvXuy/jpOBI3eu35ujci50tkqYHHwwg+8=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8/go.mod h1:bPVnJ5gJ0WmUfN1bJP9Cis0ab8SSByx6JVzyLJjDMwA=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24 h1:LsmPeFvj4GhiV5Y7Rm8I845XysdxVN4MQmfZ36P5bmw=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220914100102-057dd738a7f7 h1:zdvFDYMz8s0e9UmOxMk0wNGOKh64KfeWpx8UAbJJI60=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220914100102-057dd738a7f7/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
||||
@@ -296,8 +296,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc h1:dpclq5m2YrqPGStKmtw7IcNbKLfbIqKXvNxDJKdIKYc=
|
||||
golang.org/x/sys v0.0.0-20220913120320-3275c407cedc/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestHysteriaInbound(t *testing.T) {
|
||||
caPem: "/etc/hysteria/ca.pem",
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
testSuitSimple1(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestHysteriaOutbound(t *testing.T) {
|
||||
@@ -162,5 +162,5 @@ func TestHysteriaOutbound(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
testSuitSimple1(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ func TestVMessQUICSelf(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
testSuitSimple1(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportOptions) {
|
||||
|
||||
9
transport/clashssr/obfs/base.go
Normal file
9
transport/clashssr/obfs/base.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package obfs
|
||||
|
||||
type Base struct {
|
||||
Host string
|
||||
Port int
|
||||
Key []byte
|
||||
IVSize int
|
||||
Param string
|
||||
}
|
||||
9
transport/clashssr/obfs/http_post.go
Normal file
9
transport/clashssr/obfs/http_post.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package obfs
|
||||
|
||||
func init() {
|
||||
register("http_post", newHTTPPost, 0)
|
||||
}
|
||||
|
||||
func newHTTPPost(b *Base) Obfs {
|
||||
return &httpObfs{Base: b, post: true}
|
||||
}
|
||||
405
transport/clashssr/obfs/http_simple.go
Normal file
405
transport/clashssr/obfs/http_simple.go
Normal file
@@ -0,0 +1,405 @@
|
||||
package obfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("http_simple", newHTTPSimple, 0)
|
||||
}
|
||||
|
||||
type httpObfs struct {
|
||||
*Base
|
||||
post bool
|
||||
}
|
||||
|
||||
func newHTTPSimple(b *Base) Obfs {
|
||||
return &httpObfs{Base: b}
|
||||
}
|
||||
|
||||
type httpConn struct {
|
||||
net.Conn
|
||||
*httpObfs
|
||||
hasSentHeader bool
|
||||
hasRecvHeader bool
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (h *httpObfs) StreamConn(c net.Conn) net.Conn {
|
||||
return &httpConn{Conn: c, httpObfs: h}
|
||||
}
|
||||
|
||||
func (c *httpConn) Read(b []byte) (int, error) {
|
||||
if c.buf != nil {
|
||||
n := copy(b, c.buf)
|
||||
if n == len(c.buf) {
|
||||
c.buf = nil
|
||||
} else {
|
||||
c.buf = c.buf[n:]
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
if c.hasRecvHeader {
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
defer pool.Put(buf)
|
||||
n, err := c.Conn.Read(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
pos := bytes.Index(buf[:n], []byte("\r\n\r\n"))
|
||||
if pos == -1 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
c.hasRecvHeader = true
|
||||
dataLength := n - pos - 4
|
||||
n = copy(b, buf[4+pos:n])
|
||||
if dataLength > n {
|
||||
c.buf = append(c.buf, buf[4+pos+n:4+pos+dataLength]...)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *httpConn) Write(b []byte) (int, error) {
|
||||
if c.hasSentHeader {
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
// 30: head length
|
||||
headLength := c.IVSize + 30
|
||||
|
||||
bLength := len(b)
|
||||
headDataLength := bLength
|
||||
if bLength-headLength > 64 {
|
||||
headDataLength = headLength + rand.Intn(65)
|
||||
}
|
||||
headData := b[:headDataLength]
|
||||
b = b[headDataLength:]
|
||||
|
||||
var body string
|
||||
host := c.Host
|
||||
if len(c.Param) > 0 {
|
||||
pos := strings.Index(c.Param, "#")
|
||||
if pos != -1 {
|
||||
body = strings.ReplaceAll(c.Param[pos+1:], "\n", "\r\n")
|
||||
body = strings.ReplaceAll(body, "\\n", "\r\n")
|
||||
host = c.Param[:pos]
|
||||
} else {
|
||||
host = c.Param
|
||||
}
|
||||
}
|
||||
hosts := strings.Split(host, ",")
|
||||
host = hosts[rand.Intn(len(hosts))]
|
||||
|
||||
buf := pool.GetBuffer()
|
||||
defer pool.PutBuffer(buf)
|
||||
if c.post {
|
||||
buf.WriteString("POST /")
|
||||
} else {
|
||||
buf.WriteString("GET /")
|
||||
}
|
||||
packURLEncodedHeadData(buf, headData)
|
||||
buf.WriteString(" HTTP/1.1\r\nHost: " + host)
|
||||
if c.Port != 80 {
|
||||
buf.WriteString(":" + strconv.Itoa(c.Port))
|
||||
}
|
||||
buf.WriteString("\r\n")
|
||||
if len(body) > 0 {
|
||||
buf.WriteString(body + "\r\n\r\n")
|
||||
} else {
|
||||
buf.WriteString("User-Agent: ")
|
||||
buf.WriteString(userAgent[rand.Intn(len(userAgent))])
|
||||
buf.WriteString("\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n")
|
||||
if c.post {
|
||||
packBoundary(buf)
|
||||
}
|
||||
buf.WriteString("DNT: 1\r\nConnection: keep-alive\r\n\r\n")
|
||||
}
|
||||
buf.Write(b)
|
||||
_, err := c.Conn.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
c.hasSentHeader = true
|
||||
return bLength, nil
|
||||
}
|
||||
|
||||
func packURLEncodedHeadData(buf *bytes.Buffer, data []byte) {
|
||||
dataLength := len(data)
|
||||
for i := 0; i < dataLength; i++ {
|
||||
buf.WriteRune('%')
|
||||
buf.WriteString(hex.EncodeToString(data[i : i+1]))
|
||||
}
|
||||
}
|
||||
|
||||
func packBoundary(buf *bytes.Buffer) {
|
||||
buf.WriteString("Content-Type: multipart/form-data; boundary=")
|
||||
set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
for i := 0; i < 32; i++ {
|
||||
buf.WriteByte(set[rand.Intn(62)])
|
||||
}
|
||||
buf.WriteString("\r\n")
|
||||
}
|
||||
|
||||
var userAgent = []string{
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; Moto C Build/NRD90M.059) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1.1; SM-J120M Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; Moto G (5) Build/NPPS25.137-93-14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G570M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 5.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; CAM-L03 Build/HUAWEICAM-L03) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.237 Safari/534.10",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Datanyze; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1.1; SM-J111M Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-J700M Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.63 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Slackware/Chrome/12.0.742.100 Safari/534.30",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; WAS-LX3 Build/HUAWEIWAS-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.1805 Safari/537.36 MVisionPlayer/1.0.0.0",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; TRT-LX3 Build/HUAWEITRT-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; vivo 1610 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; de-de; SAMSUNG GT-I9195 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX3 Build/HUAWEIANE-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; U; Linux i586; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G610M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-J500M Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; vivo 1606 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G610M Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1; vivo 1716 Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G570M Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; MYA-L22 Build/HUAWEIMYA-L22) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1; A1601 Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; TRT-LX2 Build/HUAWEITRT-LX2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/59.0.3071.125 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; CAM-L21 Build/HUAWEICAM-L21; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.2; Redmi 4X Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; SM-G7102 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1; HUAWEI CUN-L22 Build/HUAWEICUN-L22; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1.1; A37fw Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-J730GM Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G610F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.2; Redmi Note 5A Build/N2G47H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; Redmi Note 4 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36",
|
||||
"Mozilla/5.0 (Unknown; Linux) AppleWebKit/538.1 (KHTML, like Gecko) Chrome/v1.0.0 Safari/538.1",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; BLL-L22 Build/HUAWEIBLL-L22) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SM-J710F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.1; CPH1723 Build/N6F26Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX3 Build/HUAWEIFIG-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.63 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1; Mi A1 Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36 MVisionPlayer/1.0.0.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1; A37f Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; CPH1607 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; vivo 1603 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; Redmi 4A Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.116 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532G Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.83 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; vivo 1713 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
|
||||
}
|
||||
42
transport/clashssr/obfs/obfs.go
Normal file
42
transport/clashssr/obfs/obfs.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package obfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
errTLS12TicketAuthIncorrectMagicNumber = errors.New("tls1.2_ticket_auth incorrect magic number")
|
||||
errTLS12TicketAuthTooShortData = errors.New("tls1.2_ticket_auth too short data")
|
||||
errTLS12TicketAuthHMACError = errors.New("tls1.2_ticket_auth hmac verifying failed")
|
||||
)
|
||||
|
||||
type authData struct {
|
||||
clientID [32]byte
|
||||
}
|
||||
|
||||
type Obfs interface {
|
||||
StreamConn(net.Conn) net.Conn
|
||||
}
|
||||
|
||||
type obfsCreator func(b *Base) Obfs
|
||||
|
||||
var obfsList = make(map[string]struct {
|
||||
overhead int
|
||||
new obfsCreator
|
||||
})
|
||||
|
||||
func register(name string, c obfsCreator, o int) {
|
||||
obfsList[name] = struct {
|
||||
overhead int
|
||||
new obfsCreator
|
||||
}{overhead: o, new: c}
|
||||
}
|
||||
|
||||
func PickObfs(name string, b *Base) (Obfs, int, error) {
|
||||
if choice, ok := obfsList[name]; ok {
|
||||
return choice.new(b), choice.overhead, nil
|
||||
}
|
||||
return nil, 0, fmt.Errorf("Obfs %s not supported", name)
|
||||
}
|
||||
15
transport/clashssr/obfs/plain.go
Normal file
15
transport/clashssr/obfs/plain.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package obfs
|
||||
|
||||
import "net"
|
||||
|
||||
type plain struct{}
|
||||
|
||||
func init() {
|
||||
register("plain", newPlain, 0)
|
||||
}
|
||||
|
||||
func newPlain(b *Base) Obfs {
|
||||
return &plain{}
|
||||
}
|
||||
|
||||
func (p *plain) StreamConn(c net.Conn) net.Conn { return c }
|
||||
71
transport/clashssr/obfs/random_head.go
Normal file
71
transport/clashssr/obfs/random_head.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package obfs
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"hash/crc32"
|
||||
"math/rand"
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("random_head", newRandomHead, 0)
|
||||
}
|
||||
|
||||
type randomHead struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
func newRandomHead(b *Base) Obfs {
|
||||
return &randomHead{Base: b}
|
||||
}
|
||||
|
||||
type randomHeadConn struct {
|
||||
net.Conn
|
||||
*randomHead
|
||||
hasSentHeader bool
|
||||
rawTransSent bool
|
||||
rawTransRecv bool
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (r *randomHead) StreamConn(c net.Conn) net.Conn {
|
||||
return &randomHeadConn{Conn: c, randomHead: r}
|
||||
}
|
||||
|
||||
func (c *randomHeadConn) Read(b []byte) (int, error) {
|
||||
if c.rawTransRecv {
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
defer pool.Put(buf)
|
||||
c.Conn.Read(buf)
|
||||
c.rawTransRecv = true
|
||||
c.Write(nil)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (c *randomHeadConn) Write(b []byte) (int, error) {
|
||||
if c.rawTransSent {
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
c.buf = append(c.buf, b...)
|
||||
if !c.hasSentHeader {
|
||||
c.hasSentHeader = true
|
||||
dataLength := rand.Intn(96) + 4
|
||||
buf := pool.Get(dataLength + 4)
|
||||
defer pool.Put(buf)
|
||||
rand.Read(buf[:dataLength])
|
||||
binary.LittleEndian.PutUint32(buf[dataLength:], 0xffffffff-crc32.ChecksumIEEE(buf[:dataLength]))
|
||||
_, err := c.Conn.Write(buf)
|
||||
return len(b), err
|
||||
}
|
||||
if c.rawTransRecv {
|
||||
_, err := c.Conn.Write(c.buf)
|
||||
c.buf = nil
|
||||
c.rawTransSent = true
|
||||
return len(b), err
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
226
transport/clashssr/obfs/tls1.2_ticket_auth.go
Normal file
226
transport/clashssr/obfs/tls1.2_ticket_auth.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package obfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("tls1.2_ticket_auth", newTLS12Ticket, 5)
|
||||
register("tls1.2_ticket_fastauth", newTLS12Ticket, 5)
|
||||
}
|
||||
|
||||
type tls12Ticket struct {
|
||||
*Base
|
||||
*authData
|
||||
}
|
||||
|
||||
func newTLS12Ticket(b *Base) Obfs {
|
||||
r := &tls12Ticket{Base: b, authData: &authData{}}
|
||||
rand.Read(r.clientID[:])
|
||||
return r
|
||||
}
|
||||
|
||||
type tls12TicketConn struct {
|
||||
net.Conn
|
||||
*tls12Ticket
|
||||
handshakeStatus int
|
||||
decoded bytes.Buffer
|
||||
underDecoded bytes.Buffer
|
||||
sendBuf bytes.Buffer
|
||||
}
|
||||
|
||||
func (t *tls12Ticket) StreamConn(c net.Conn) net.Conn {
|
||||
return &tls12TicketConn{Conn: c, tls12Ticket: t}
|
||||
}
|
||||
|
||||
func (c *tls12TicketConn) Read(b []byte) (int, error) {
|
||||
if c.decoded.Len() > 0 {
|
||||
return c.decoded.Read(b)
|
||||
}
|
||||
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
defer pool.Put(buf)
|
||||
n, err := c.Conn.Read(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if c.handshakeStatus == 8 {
|
||||
c.underDecoded.Write(buf[:n])
|
||||
for c.underDecoded.Len() > 5 {
|
||||
if !bytes.Equal(c.underDecoded.Bytes()[:3], []byte{0x17, 3, 3}) {
|
||||
c.underDecoded.Reset()
|
||||
return 0, errTLS12TicketAuthIncorrectMagicNumber
|
||||
}
|
||||
size := int(binary.BigEndian.Uint16(c.underDecoded.Bytes()[3:5]))
|
||||
if c.underDecoded.Len() < 5+size {
|
||||
break
|
||||
}
|
||||
c.underDecoded.Next(5)
|
||||
c.decoded.Write(c.underDecoded.Next(size))
|
||||
}
|
||||
n, _ = c.decoded.Read(b)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
if n < 11+32+1+32 {
|
||||
return 0, errTLS12TicketAuthTooShortData
|
||||
}
|
||||
|
||||
if !hmac.Equal(buf[33:43], c.hmacSHA1(buf[11:33])[:10]) || !hmac.Equal(buf[n-10:n], c.hmacSHA1(buf[:n-10])[:10]) {
|
||||
return 0, errTLS12TicketAuthHMACError
|
||||
}
|
||||
|
||||
c.Write(nil)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (c *tls12TicketConn) Write(b []byte) (int, error) {
|
||||
length := len(b)
|
||||
if c.handshakeStatus == 8 {
|
||||
buf := pool.GetBuffer()
|
||||
defer pool.PutBuffer(buf)
|
||||
for len(b) > 2048 {
|
||||
size := rand.Intn(4096) + 100
|
||||
if len(b) < size {
|
||||
size = len(b)
|
||||
}
|
||||
packData(buf, b[:size])
|
||||
b = b[size:]
|
||||
}
|
||||
if len(b) > 0 {
|
||||
packData(buf, b)
|
||||
}
|
||||
_, err := c.Conn.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return length, nil
|
||||
}
|
||||
|
||||
if len(b) > 0 {
|
||||
packData(&c.sendBuf, b)
|
||||
}
|
||||
|
||||
if c.handshakeStatus == 0 {
|
||||
c.handshakeStatus = 1
|
||||
|
||||
data := pool.GetBuffer()
|
||||
defer pool.PutBuffer(data)
|
||||
|
||||
data.Write([]byte{3, 3})
|
||||
c.packAuthData(data)
|
||||
data.WriteByte(0x20)
|
||||
data.Write(c.clientID[:])
|
||||
data.Write([]byte{0x00, 0x1c, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13, 0xc0, 0x0a, 0xc0, 0x14, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x9c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0x0a})
|
||||
data.Write([]byte{0x1, 0x0})
|
||||
|
||||
ext := pool.GetBuffer()
|
||||
defer pool.PutBuffer(ext)
|
||||
|
||||
host := c.getHost()
|
||||
ext.Write([]byte{0xff, 0x01, 0x00, 0x01, 0x00})
|
||||
packSNIData(ext, host)
|
||||
ext.Write([]byte{0, 0x17, 0, 0})
|
||||
c.packTicketBuf(ext, host)
|
||||
ext.Write([]byte{0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x06, 0x01, 0x06, 0x03, 0x05, 0x01, 0x05, 0x03, 0x04, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x03, 0x02, 0x01, 0x02, 0x03})
|
||||
ext.Write([]byte{0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00})
|
||||
ext.Write([]byte{0x00, 0x12, 0x00, 0x00})
|
||||
ext.Write([]byte{0x75, 0x50, 0x00, 0x00})
|
||||
ext.Write([]byte{0x00, 0x0b, 0x00, 0x02, 0x01, 0x00})
|
||||
ext.Write([]byte{0x00, 0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x17, 0x00, 0x18})
|
||||
|
||||
binary.Write(data, binary.BigEndian, uint16(ext.Len()))
|
||||
data.ReadFrom(ext)
|
||||
|
||||
ret := pool.GetBuffer()
|
||||
defer pool.PutBuffer(ret)
|
||||
|
||||
ret.Write([]byte{0x16, 3, 1})
|
||||
binary.Write(ret, binary.BigEndian, uint16(data.Len()+4))
|
||||
ret.Write([]byte{1, 0})
|
||||
binary.Write(ret, binary.BigEndian, uint16(data.Len()))
|
||||
ret.ReadFrom(data)
|
||||
|
||||
_, err := c.Conn.Write(ret.Bytes())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return length, nil
|
||||
} else if c.handshakeStatus == 1 && len(b) == 0 {
|
||||
buf := pool.GetBuffer()
|
||||
defer pool.PutBuffer(buf)
|
||||
|
||||
buf.Write([]byte{0x14, 3, 3, 0, 1, 1, 0x16, 3, 3, 0, 0x20})
|
||||
tools.AppendRandBytes(buf, 22)
|
||||
buf.Write(c.hmacSHA1(buf.Bytes())[:10])
|
||||
buf.ReadFrom(&c.sendBuf)
|
||||
|
||||
c.handshakeStatus = 8
|
||||
|
||||
_, err := c.Conn.Write(buf.Bytes())
|
||||
return 0, err
|
||||
}
|
||||
return length, nil
|
||||
}
|
||||
|
||||
func packData(buf *bytes.Buffer, data []byte) {
|
||||
buf.Write([]byte{0x17, 3, 3})
|
||||
binary.Write(buf, binary.BigEndian, uint16(len(data)))
|
||||
buf.Write(data)
|
||||
}
|
||||
|
||||
func (t *tls12Ticket) packAuthData(buf *bytes.Buffer) {
|
||||
binary.Write(buf, binary.BigEndian, uint32(time.Now().Unix()))
|
||||
tools.AppendRandBytes(buf, 18)
|
||||
buf.Write(t.hmacSHA1(buf.Bytes()[buf.Len()-22:])[:10])
|
||||
}
|
||||
|
||||
func packSNIData(buf *bytes.Buffer, u string) {
|
||||
len := uint16(len(u))
|
||||
buf.Write([]byte{0, 0})
|
||||
binary.Write(buf, binary.BigEndian, len+5)
|
||||
binary.Write(buf, binary.BigEndian, len+3)
|
||||
buf.WriteByte(0)
|
||||
binary.Write(buf, binary.BigEndian, len)
|
||||
buf.WriteString(u)
|
||||
}
|
||||
|
||||
func (c *tls12TicketConn) packTicketBuf(buf *bytes.Buffer, u string) {
|
||||
length := 16 * (rand.Intn(17) + 8)
|
||||
buf.Write([]byte{0, 0x23})
|
||||
binary.Write(buf, binary.BigEndian, uint16(length))
|
||||
tools.AppendRandBytes(buf, length)
|
||||
}
|
||||
|
||||
func (t *tls12Ticket) hmacSHA1(data []byte) []byte {
|
||||
key := pool.Get(len(t.Key) + 32)
|
||||
defer pool.Put(key)
|
||||
copy(key, t.Key)
|
||||
copy(key[len(t.Key):], t.clientID[:])
|
||||
|
||||
sha1Data := tools.HmacSHA1(key, data)
|
||||
return sha1Data[:10]
|
||||
}
|
||||
|
||||
func (t *tls12Ticket) getHost() string {
|
||||
host := t.Param
|
||||
if len(host) == 0 {
|
||||
host = t.Host
|
||||
}
|
||||
if len(host) > 0 && host[len(host)-1] >= '0' && host[len(host)-1] <= '9' {
|
||||
host = ""
|
||||
}
|
||||
hosts := strings.Split(host, ",")
|
||||
host = hosts[rand.Intn(len(hosts))]
|
||||
return host
|
||||
}
|
||||
18
transport/clashssr/protocol/auth_aes128_md5.go
Normal file
18
transport/clashssr/protocol/auth_aes128_md5.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package protocol
|
||||
|
||||
import "github.com/Dreamacro/clash/transport/ssr/tools"
|
||||
|
||||
func init() {
|
||||
register("auth_aes128_md5", newAuthAES128MD5, 9)
|
||||
}
|
||||
|
||||
func newAuthAES128MD5(b *Base) Protocol {
|
||||
a := &authAES128{
|
||||
Base: b,
|
||||
authData: &authData{},
|
||||
authAES128Function: &authAES128Function{salt: "auth_aes128_md5", hmac: tools.HmacMD5, hashDigest: tools.MD5Sum},
|
||||
userData: &userData{},
|
||||
}
|
||||
a.initUserData()
|
||||
return a
|
||||
}
|
||||
277
transport/clashssr/protocol/auth_aes128_sha1.go
Normal file
277
transport/clashssr/protocol/auth_aes128_sha1.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
||||
)
|
||||
|
||||
type (
|
||||
hmacMethod func(key, data []byte) []byte
|
||||
hashDigestMethod func([]byte) []byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("auth_aes128_sha1", newAuthAES128SHA1, 9)
|
||||
}
|
||||
|
||||
type authAES128Function struct {
|
||||
salt string
|
||||
hmac hmacMethod
|
||||
hashDigest hashDigestMethod
|
||||
}
|
||||
|
||||
type authAES128 struct {
|
||||
*Base
|
||||
*authData
|
||||
*authAES128Function
|
||||
*userData
|
||||
iv []byte
|
||||
hasSentHeader bool
|
||||
rawTrans bool
|
||||
packID uint32
|
||||
recvID uint32
|
||||
}
|
||||
|
||||
func newAuthAES128SHA1(b *Base) Protocol {
|
||||
a := &authAES128{
|
||||
Base: b,
|
||||
authData: &authData{},
|
||||
authAES128Function: &authAES128Function{salt: "auth_aes128_sha1", hmac: tools.HmacSHA1, hashDigest: tools.SHA1Sum},
|
||||
userData: &userData{},
|
||||
}
|
||||
a.initUserData()
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *authAES128) initUserData() {
|
||||
params := strings.Split(a.Param, ":")
|
||||
if len(params) > 1 {
|
||||
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
|
||||
binary.LittleEndian.PutUint32(a.userID[:], uint32(userID))
|
||||
a.userKey = a.hashDigest([]byte(params[1]))
|
||||
}
|
||||
}
|
||||
if len(a.userKey) == 0 {
|
||||
a.userKey = a.Key
|
||||
rand.Read(a.userID[:])
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authAES128) StreamConn(c net.Conn, iv []byte) net.Conn {
|
||||
p := &authAES128{
|
||||
Base: a.Base,
|
||||
authData: a.next(),
|
||||
authAES128Function: a.authAES128Function,
|
||||
userData: a.userData,
|
||||
packID: 1,
|
||||
recvID: 1,
|
||||
}
|
||||
p.iv = iv
|
||||
return &Conn{Conn: c, Protocol: p}
|
||||
}
|
||||
|
||||
func (a *authAES128) PacketConn(c net.PacketConn) net.PacketConn {
|
||||
p := &authAES128{
|
||||
Base: a.Base,
|
||||
authAES128Function: a.authAES128Function,
|
||||
userData: a.userData,
|
||||
}
|
||||
return &PacketConn{PacketConn: c, Protocol: p}
|
||||
}
|
||||
|
||||
func (a *authAES128) Decode(dst, src *bytes.Buffer) error {
|
||||
if a.rawTrans {
|
||||
dst.ReadFrom(src)
|
||||
return nil
|
||||
}
|
||||
for src.Len() > 4 {
|
||||
macKey := pool.Get(len(a.userKey) + 4)
|
||||
defer pool.Put(macKey)
|
||||
copy(macKey, a.userKey)
|
||||
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.recvID)
|
||||
if !bytes.Equal(a.hmac(macKey, src.Bytes()[:2])[:2], src.Bytes()[2:4]) {
|
||||
src.Reset()
|
||||
return errAuthAES128MACError
|
||||
}
|
||||
|
||||
length := int(binary.LittleEndian.Uint16(src.Bytes()[:2]))
|
||||
if length >= 8192 || length < 7 {
|
||||
a.rawTrans = true
|
||||
src.Reset()
|
||||
return errAuthAES128LengthError
|
||||
}
|
||||
if length > src.Len() {
|
||||
break
|
||||
}
|
||||
|
||||
if !bytes.Equal(a.hmac(macKey, src.Bytes()[:length-4])[:4], src.Bytes()[length-4:length]) {
|
||||
a.rawTrans = true
|
||||
src.Reset()
|
||||
return errAuthAES128ChksumError
|
||||
}
|
||||
|
||||
a.recvID++
|
||||
|
||||
pos := int(src.Bytes()[4])
|
||||
if pos < 255 {
|
||||
pos += 4
|
||||
} else {
|
||||
pos = int(binary.LittleEndian.Uint16(src.Bytes()[5:7])) + 4
|
||||
}
|
||||
dst.Write(src.Bytes()[pos : length-4])
|
||||
src.Next(length)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authAES128) Encode(buf *bytes.Buffer, b []byte) error {
|
||||
fullDataLength := len(b)
|
||||
if !a.hasSentHeader {
|
||||
dataLength := getDataLength(b)
|
||||
a.packAuthData(buf, b[:dataLength])
|
||||
b = b[dataLength:]
|
||||
a.hasSentHeader = true
|
||||
}
|
||||
for len(b) > 8100 {
|
||||
a.packData(buf, b[:8100], fullDataLength)
|
||||
b = b[8100:]
|
||||
}
|
||||
if len(b) > 0 {
|
||||
a.packData(buf, b, fullDataLength)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authAES128) DecodePacket(b []byte) ([]byte, error) {
|
||||
if len(b) < 4 {
|
||||
return nil, errAuthAES128LengthError
|
||||
}
|
||||
if !bytes.Equal(a.hmac(a.Key, b[:len(b)-4])[:4], b[len(b)-4:]) {
|
||||
return nil, errAuthAES128ChksumError
|
||||
}
|
||||
return b[:len(b)-4], nil
|
||||
}
|
||||
|
||||
func (a *authAES128) EncodePacket(buf *bytes.Buffer, b []byte) error {
|
||||
buf.Write(b)
|
||||
buf.Write(a.userID[:])
|
||||
buf.Write(a.hmac(a.userKey, buf.Bytes())[:4])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authAES128) packData(poolBuf *bytes.Buffer, data []byte, fullDataLength int) {
|
||||
dataLength := len(data)
|
||||
randDataLength := a.getRandDataLengthForPackData(dataLength, fullDataLength)
|
||||
/*
|
||||
2: uint16 LittleEndian packedDataLength
|
||||
2: hmac of packedDataLength
|
||||
3: maxRandDataLengthPrefix (min:1)
|
||||
4: hmac of packedData except the last 4 bytes
|
||||
*/
|
||||
packedDataLength := 2 + 2 + 3 + randDataLength + dataLength + 4
|
||||
if randDataLength < 128 {
|
||||
packedDataLength -= 2
|
||||
}
|
||||
|
||||
macKey := pool.Get(len(a.userKey) + 4)
|
||||
defer pool.Put(macKey)
|
||||
copy(macKey, a.userKey)
|
||||
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.packID)
|
||||
a.packID++
|
||||
|
||||
binary.Write(poolBuf, binary.LittleEndian, uint16(packedDataLength))
|
||||
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[poolBuf.Len()-2:])[:2])
|
||||
a.packRandData(poolBuf, randDataLength)
|
||||
poolBuf.Write(data)
|
||||
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[poolBuf.Len()-packedDataLength+4:])[:4])
|
||||
}
|
||||
|
||||
func trapezoidRandom(max int, d float64) int {
|
||||
base := rand.Float64()
|
||||
if d-0 > 1e-6 {
|
||||
a := 1 - d
|
||||
base = (math.Sqrt(a*a+4*d*base) - a) / (2 * d)
|
||||
}
|
||||
return int(base * float64(max))
|
||||
}
|
||||
|
||||
func (a *authAES128) getRandDataLengthForPackData(dataLength, fullDataLength int) int {
|
||||
if fullDataLength >= 32*1024-a.Overhead {
|
||||
return 0
|
||||
}
|
||||
// 1460: tcp_mss
|
||||
revLength := 1460 - dataLength - 9
|
||||
if revLength == 0 {
|
||||
return 0
|
||||
}
|
||||
if revLength < 0 {
|
||||
if revLength > -1460 {
|
||||
return trapezoidRandom(revLength+1460, -0.3)
|
||||
}
|
||||
return rand.Intn(32)
|
||||
}
|
||||
if dataLength > 900 {
|
||||
return rand.Intn(revLength)
|
||||
}
|
||||
return trapezoidRandom(revLength, -0.3)
|
||||
}
|
||||
|
||||
func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
dataLength := len(data)
|
||||
randDataLength := a.getRandDataLengthForPackAuthData(dataLength)
|
||||
/*
|
||||
7: checkHead(1) and hmac of checkHead(6)
|
||||
4: userID
|
||||
16: encrypted data of authdata(12), uint16 BigEndian packedDataLength(2) and uint16 BigEndian randDataLength(2)
|
||||
4: hmac of userID and encrypted data
|
||||
4: hmac of packedAuthData except the last 4 bytes
|
||||
*/
|
||||
packedAuthDataLength := 7 + 4 + 16 + 4 + randDataLength + dataLength + 4
|
||||
|
||||
macKey := pool.Get(len(a.iv) + len(a.Key))
|
||||
defer pool.Put(macKey)
|
||||
copy(macKey, a.iv)
|
||||
copy(macKey[len(a.iv):], a.Key)
|
||||
|
||||
poolBuf.WriteByte(byte(rand.Intn(256)))
|
||||
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes())[:6])
|
||||
poolBuf.Write(a.userID[:])
|
||||
err := a.authData.putEncryptedData(poolBuf, a.userKey, [2]int{packedAuthDataLength, randDataLength}, a.salt)
|
||||
if err != nil {
|
||||
poolBuf.Reset()
|
||||
return
|
||||
}
|
||||
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[7:])[:4])
|
||||
tools.AppendRandBytes(poolBuf, randDataLength)
|
||||
poolBuf.Write(data)
|
||||
poolBuf.Write(a.hmac(a.userKey, poolBuf.Bytes())[:4])
|
||||
}
|
||||
|
||||
func (a *authAES128) getRandDataLengthForPackAuthData(size int) int {
|
||||
if size > 400 {
|
||||
return rand.Intn(512)
|
||||
}
|
||||
return rand.Intn(1024)
|
||||
}
|
||||
|
||||
func (a *authAES128) packRandData(poolBuf *bytes.Buffer, size int) {
|
||||
if size < 128 {
|
||||
poolBuf.WriteByte(byte(size + 1))
|
||||
tools.AppendRandBytes(poolBuf, size)
|
||||
return
|
||||
}
|
||||
poolBuf.WriteByte(255)
|
||||
binary.Write(poolBuf, binary.LittleEndian, uint16(size+3))
|
||||
tools.AppendRandBytes(poolBuf, size)
|
||||
}
|
||||
306
transport/clashssr/protocol/auth_chain_a.go
Normal file
306
transport/clashssr/protocol/auth_chain_a.go
Normal file
@@ -0,0 +1,306 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/rc4"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("auth_chain_a", newAuthChainA, 4)
|
||||
}
|
||||
|
||||
type randDataLengthMethod func(int, []byte, *tools.XorShift128Plus) int
|
||||
|
||||
type authChainA struct {
|
||||
*Base
|
||||
*authData
|
||||
*userData
|
||||
iv []byte
|
||||
salt string
|
||||
hasSentHeader bool
|
||||
rawTrans bool
|
||||
lastClientHash []byte
|
||||
lastServerHash []byte
|
||||
encrypter cipher.Stream
|
||||
decrypter cipher.Stream
|
||||
randomClient tools.XorShift128Plus
|
||||
randomServer tools.XorShift128Plus
|
||||
randDataLength randDataLengthMethod
|
||||
packID uint32
|
||||
recvID uint32
|
||||
}
|
||||
|
||||
func newAuthChainA(b *Base) Protocol {
|
||||
a := &authChainA{
|
||||
Base: b,
|
||||
authData: &authData{},
|
||||
userData: &userData{},
|
||||
salt: "auth_chain_a",
|
||||
}
|
||||
a.initUserData()
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *authChainA) initUserData() {
|
||||
params := strings.Split(a.Param, ":")
|
||||
if len(params) > 1 {
|
||||
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
|
||||
binary.LittleEndian.PutUint32(a.userID[:], uint32(userID))
|
||||
a.userKey = []byte(params[1])
|
||||
}
|
||||
}
|
||||
if len(a.userKey) == 0 {
|
||||
a.userKey = a.Key
|
||||
rand.Read(a.userID[:])
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authChainA) StreamConn(c net.Conn, iv []byte) net.Conn {
|
||||
p := &authChainA{
|
||||
Base: a.Base,
|
||||
authData: a.next(),
|
||||
userData: a.userData,
|
||||
salt: a.salt,
|
||||
packID: 1,
|
||||
recvID: 1,
|
||||
}
|
||||
p.iv = iv
|
||||
p.randDataLength = p.getRandLength
|
||||
return &Conn{Conn: c, Protocol: p}
|
||||
}
|
||||
|
||||
func (a *authChainA) PacketConn(c net.PacketConn) net.PacketConn {
|
||||
p := &authChainA{
|
||||
Base: a.Base,
|
||||
salt: a.salt,
|
||||
userData: a.userData,
|
||||
}
|
||||
return &PacketConn{PacketConn: c, Protocol: p}
|
||||
}
|
||||
|
||||
func (a *authChainA) Decode(dst, src *bytes.Buffer) error {
|
||||
if a.rawTrans {
|
||||
dst.ReadFrom(src)
|
||||
return nil
|
||||
}
|
||||
for src.Len() > 4 {
|
||||
macKey := pool.Get(len(a.userKey) + 4)
|
||||
defer pool.Put(macKey)
|
||||
copy(macKey, a.userKey)
|
||||
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.recvID)
|
||||
|
||||
dataLength := int(binary.LittleEndian.Uint16(src.Bytes()[:2]) ^ binary.LittleEndian.Uint16(a.lastServerHash[14:16]))
|
||||
randDataLength := a.randDataLength(dataLength, a.lastServerHash, &a.randomServer)
|
||||
length := dataLength + randDataLength
|
||||
|
||||
if length >= 4096 {
|
||||
a.rawTrans = true
|
||||
src.Reset()
|
||||
return errAuthChainLengthError
|
||||
}
|
||||
|
||||
if 4+length > src.Len() {
|
||||
break
|
||||
}
|
||||
|
||||
serverHash := tools.HmacMD5(macKey, src.Bytes()[:length+2])
|
||||
if !bytes.Equal(serverHash[:2], src.Bytes()[length+2:length+4]) {
|
||||
a.rawTrans = true
|
||||
src.Reset()
|
||||
return errAuthChainChksumError
|
||||
}
|
||||
a.lastServerHash = serverHash
|
||||
|
||||
pos := 2
|
||||
if dataLength > 0 && randDataLength > 0 {
|
||||
pos += getRandStartPos(randDataLength, &a.randomServer)
|
||||
}
|
||||
wantedData := src.Bytes()[pos : pos+dataLength]
|
||||
a.decrypter.XORKeyStream(wantedData, wantedData)
|
||||
if a.recvID == 1 {
|
||||
dst.Write(wantedData[2:])
|
||||
} else {
|
||||
dst.Write(wantedData)
|
||||
}
|
||||
a.recvID++
|
||||
src.Next(length + 4)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authChainA) Encode(buf *bytes.Buffer, b []byte) error {
|
||||
if !a.hasSentHeader {
|
||||
dataLength := getDataLength(b)
|
||||
a.packAuthData(buf, b[:dataLength])
|
||||
b = b[dataLength:]
|
||||
a.hasSentHeader = true
|
||||
}
|
||||
for len(b) > 2800 {
|
||||
a.packData(buf, b[:2800])
|
||||
b = b[2800:]
|
||||
}
|
||||
if len(b) > 0 {
|
||||
a.packData(buf, b)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authChainA) DecodePacket(b []byte) ([]byte, error) {
|
||||
if len(b) < 9 {
|
||||
return nil, errAuthChainLengthError
|
||||
}
|
||||
if !bytes.Equal(tools.HmacMD5(a.userKey, b[:len(b)-1])[:1], b[len(b)-1:]) {
|
||||
return nil, errAuthChainChksumError
|
||||
}
|
||||
md5Data := tools.HmacMD5(a.Key, b[len(b)-8:len(b)-1])
|
||||
|
||||
randDataLength := udpGetRandLength(md5Data, &a.randomServer)
|
||||
|
||||
key := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(md5Data), 16)
|
||||
rc4Cipher, err := rc4.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wantedData := b[:len(b)-8-randDataLength]
|
||||
rc4Cipher.XORKeyStream(wantedData, wantedData)
|
||||
return wantedData, nil
|
||||
}
|
||||
|
||||
func (a *authChainA) EncodePacket(buf *bytes.Buffer, b []byte) error {
|
||||
authData := pool.Get(3)
|
||||
defer pool.Put(authData)
|
||||
rand.Read(authData)
|
||||
|
||||
md5Data := tools.HmacMD5(a.Key, authData)
|
||||
|
||||
randDataLength := udpGetRandLength(md5Data, &a.randomClient)
|
||||
|
||||
key := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(md5Data), 16)
|
||||
rc4Cipher, err := rc4.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rc4Cipher.XORKeyStream(b, b)
|
||||
|
||||
buf.Write(b)
|
||||
tools.AppendRandBytes(buf, randDataLength)
|
||||
buf.Write(authData)
|
||||
binary.Write(buf, binary.LittleEndian, binary.LittleEndian.Uint32(a.userID[:])^binary.LittleEndian.Uint32(md5Data[:4]))
|
||||
buf.Write(tools.HmacMD5(a.userKey, buf.Bytes())[:1])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authChainA) packAuthData(poolBuf *bytes.Buffer, data []byte) {
|
||||
/*
|
||||
dataLength := len(data)
|
||||
12: checkHead(4) and hmac of checkHead(8)
|
||||
4: uint32 LittleEndian uid (uid = userID ^ last client hash)
|
||||
16: encrypted data of authdata(12), uint16 LittleEndian overhead(2) and uint16 LittleEndian number zero(2)
|
||||
4: last server hash(4)
|
||||
packedAuthDataLength := 12 + 4 + 16 + 4 + dataLength
|
||||
*/
|
||||
|
||||
macKey := pool.Get(len(a.iv) + len(a.Key))
|
||||
defer pool.Put(macKey)
|
||||
copy(macKey, a.iv)
|
||||
copy(macKey[len(a.iv):], a.Key)
|
||||
|
||||
// check head
|
||||
tools.AppendRandBytes(poolBuf, 4)
|
||||
a.lastClientHash = tools.HmacMD5(macKey, poolBuf.Bytes())
|
||||
a.initRC4Cipher()
|
||||
poolBuf.Write(a.lastClientHash[:8])
|
||||
// uid
|
||||
binary.Write(poolBuf, binary.LittleEndian, binary.LittleEndian.Uint32(a.userID[:])^binary.LittleEndian.Uint32(a.lastClientHash[8:12]))
|
||||
// encrypted data
|
||||
err := a.putEncryptedData(poolBuf, a.userKey, [2]int{a.Overhead, 0}, a.salt)
|
||||
if err != nil {
|
||||
poolBuf.Reset()
|
||||
return
|
||||
}
|
||||
// last server hash
|
||||
a.lastServerHash = tools.HmacMD5(a.userKey, poolBuf.Bytes()[12:])
|
||||
poolBuf.Write(a.lastServerHash[:4])
|
||||
// packed data
|
||||
a.packData(poolBuf, data)
|
||||
}
|
||||
|
||||
func (a *authChainA) packData(poolBuf *bytes.Buffer, data []byte) {
|
||||
a.encrypter.XORKeyStream(data, data)
|
||||
|
||||
macKey := pool.Get(len(a.userKey) + 4)
|
||||
defer pool.Put(macKey)
|
||||
copy(macKey, a.userKey)
|
||||
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.packID)
|
||||
a.packID++
|
||||
|
||||
length := uint16(len(data)) ^ binary.LittleEndian.Uint16(a.lastClientHash[14:16])
|
||||
|
||||
originalLength := poolBuf.Len()
|
||||
binary.Write(poolBuf, binary.LittleEndian, length)
|
||||
a.putMixedRandDataAndData(poolBuf, data)
|
||||
a.lastClientHash = tools.HmacMD5(macKey, poolBuf.Bytes()[originalLength:])
|
||||
poolBuf.Write(a.lastClientHash[:2])
|
||||
}
|
||||
|
||||
func (a *authChainA) putMixedRandDataAndData(poolBuf *bytes.Buffer, data []byte) {
|
||||
randDataLength := a.randDataLength(len(data), a.lastClientHash, &a.randomClient)
|
||||
if len(data) == 0 {
|
||||
tools.AppendRandBytes(poolBuf, randDataLength)
|
||||
return
|
||||
}
|
||||
if randDataLength > 0 {
|
||||
startPos := getRandStartPos(randDataLength, &a.randomClient)
|
||||
tools.AppendRandBytes(poolBuf, startPos)
|
||||
poolBuf.Write(data)
|
||||
tools.AppendRandBytes(poolBuf, randDataLength-startPos)
|
||||
return
|
||||
}
|
||||
poolBuf.Write(data)
|
||||
}
|
||||
|
||||
func getRandStartPos(length int, random *tools.XorShift128Plus) int {
|
||||
if length == 0 {
|
||||
return 0
|
||||
}
|
||||
return int(int64(random.Next()%8589934609) % int64(length))
|
||||
}
|
||||
|
||||
func (a *authChainA) getRandLength(length int, lastHash []byte, random *tools.XorShift128Plus) int {
|
||||
if length > 1440 {
|
||||
return 0
|
||||
}
|
||||
random.InitFromBinAndLength(lastHash, length)
|
||||
if length > 1300 {
|
||||
return int(random.Next() % 31)
|
||||
}
|
||||
if length > 900 {
|
||||
return int(random.Next() % 127)
|
||||
}
|
||||
if length > 400 {
|
||||
return int(random.Next() % 521)
|
||||
}
|
||||
return int(random.Next() % 1021)
|
||||
}
|
||||
|
||||
func (a *authChainA) initRC4Cipher() {
|
||||
key := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(a.lastClientHash), 16)
|
||||
a.encrypter, _ = rc4.NewCipher(key)
|
||||
a.decrypter, _ = rc4.NewCipher(key)
|
||||
}
|
||||
|
||||
func udpGetRandLength(lastHash []byte, random *tools.XorShift128Plus) int {
|
||||
random.InitFromBin(lastHash)
|
||||
return int(random.Next() % 127)
|
||||
}
|
||||
97
transport/clashssr/protocol/auth_chain_b.go
Normal file
97
transport/clashssr/protocol/auth_chain_b.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sort"
|
||||
|
||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("auth_chain_b", newAuthChainB, 4)
|
||||
}
|
||||
|
||||
type authChainB struct {
|
||||
*authChainA
|
||||
dataSizeList []int
|
||||
dataSizeList2 []int
|
||||
}
|
||||
|
||||
func newAuthChainB(b *Base) Protocol {
|
||||
a := &authChainB{
|
||||
authChainA: &authChainA{
|
||||
Base: b,
|
||||
authData: &authData{},
|
||||
userData: &userData{},
|
||||
salt: "auth_chain_b",
|
||||
},
|
||||
}
|
||||
a.initUserData()
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *authChainB) StreamConn(c net.Conn, iv []byte) net.Conn {
|
||||
p := &authChainB{
|
||||
authChainA: &authChainA{
|
||||
Base: a.Base,
|
||||
authData: a.next(),
|
||||
userData: a.userData,
|
||||
salt: a.salt,
|
||||
packID: 1,
|
||||
recvID: 1,
|
||||
},
|
||||
}
|
||||
p.iv = iv
|
||||
p.randDataLength = p.getRandLength
|
||||
p.initDataSize()
|
||||
return &Conn{Conn: c, Protocol: p}
|
||||
}
|
||||
|
||||
func (a *authChainB) initDataSize() {
|
||||
a.dataSizeList = a.dataSizeList[:0]
|
||||
a.dataSizeList2 = a.dataSizeList2[:0]
|
||||
|
||||
a.randomServer.InitFromBin(a.Key)
|
||||
length := a.randomServer.Next()%8 + 4
|
||||
for ; length > 0; length-- {
|
||||
a.dataSizeList = append(a.dataSizeList, int(a.randomServer.Next()%2340%2040%1440))
|
||||
}
|
||||
sort.Ints(a.dataSizeList)
|
||||
|
||||
length = a.randomServer.Next()%16 + 8
|
||||
for ; length > 0; length-- {
|
||||
a.dataSizeList2 = append(a.dataSizeList2, int(a.randomServer.Next()%2340%2040%1440))
|
||||
}
|
||||
sort.Ints(a.dataSizeList2)
|
||||
}
|
||||
|
||||
func (a *authChainB) getRandLength(length int, lashHash []byte, random *tools.XorShift128Plus) int {
|
||||
if length >= 1440 {
|
||||
return 0
|
||||
}
|
||||
random.InitFromBinAndLength(lashHash, length)
|
||||
pos := sort.Search(len(a.dataSizeList), func(i int) bool { return a.dataSizeList[i] >= length+a.Overhead })
|
||||
finalPos := pos + int(random.Next()%uint64(len(a.dataSizeList)))
|
||||
if finalPos < len(a.dataSizeList) {
|
||||
return a.dataSizeList[finalPos] - length - a.Overhead
|
||||
}
|
||||
|
||||
pos = sort.Search(len(a.dataSizeList2), func(i int) bool { return a.dataSizeList2[i] >= length+a.Overhead })
|
||||
finalPos = pos + int(random.Next()%uint64(len(a.dataSizeList2)))
|
||||
if finalPos < len(a.dataSizeList2) {
|
||||
return a.dataSizeList2[finalPos] - length - a.Overhead
|
||||
}
|
||||
if finalPos < pos+len(a.dataSizeList2)-1 {
|
||||
return 0
|
||||
}
|
||||
if length > 1300 {
|
||||
return int(random.Next() % 31)
|
||||
}
|
||||
if length > 900 {
|
||||
return int(random.Next() % 127)
|
||||
}
|
||||
if length > 400 {
|
||||
return int(random.Next() % 521)
|
||||
}
|
||||
return int(random.Next() % 1021)
|
||||
}
|
||||
182
transport/clashssr/protocol/auth_sha1_v4.go
Normal file
182
transport/clashssr/protocol/auth_sha1_v4.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"hash/adler32"
|
||||
"hash/crc32"
|
||||
"math/rand"
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("auth_sha1_v4", newAuthSHA1V4, 7)
|
||||
}
|
||||
|
||||
type authSHA1V4 struct {
|
||||
*Base
|
||||
*authData
|
||||
iv []byte
|
||||
hasSentHeader bool
|
||||
rawTrans bool
|
||||
}
|
||||
|
||||
func newAuthSHA1V4(b *Base) Protocol {
|
||||
return &authSHA1V4{Base: b, authData: &authData{}}
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) StreamConn(c net.Conn, iv []byte) net.Conn {
|
||||
p := &authSHA1V4{Base: a.Base, authData: a.next()}
|
||||
p.iv = iv
|
||||
return &Conn{Conn: c, Protocol: p}
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) PacketConn(c net.PacketConn) net.PacketConn {
|
||||
return c
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) Decode(dst, src *bytes.Buffer) error {
|
||||
if a.rawTrans {
|
||||
dst.ReadFrom(src)
|
||||
return nil
|
||||
}
|
||||
for src.Len() > 4 {
|
||||
if uint16(crc32.ChecksumIEEE(src.Bytes()[:2])&0xffff) != binary.LittleEndian.Uint16(src.Bytes()[2:4]) {
|
||||
src.Reset()
|
||||
return errAuthSHA1V4CRC32Error
|
||||
}
|
||||
|
||||
length := int(binary.BigEndian.Uint16(src.Bytes()[:2]))
|
||||
if length >= 8192 || length < 7 {
|
||||
a.rawTrans = true
|
||||
src.Reset()
|
||||
return errAuthSHA1V4LengthError
|
||||
}
|
||||
if length > src.Len() {
|
||||
break
|
||||
}
|
||||
|
||||
if adler32.Checksum(src.Bytes()[:length-4]) != binary.LittleEndian.Uint32(src.Bytes()[length-4:length]) {
|
||||
a.rawTrans = true
|
||||
src.Reset()
|
||||
return errAuthSHA1V4Adler32Error
|
||||
}
|
||||
|
||||
pos := int(src.Bytes()[4])
|
||||
if pos < 255 {
|
||||
pos += 4
|
||||
} else {
|
||||
pos = int(binary.BigEndian.Uint16(src.Bytes()[5:7])) + 4
|
||||
}
|
||||
dst.Write(src.Bytes()[pos : length-4])
|
||||
src.Next(length)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) Encode(buf *bytes.Buffer, b []byte) error {
|
||||
if !a.hasSentHeader {
|
||||
dataLength := getDataLength(b)
|
||||
|
||||
a.packAuthData(buf, b[:dataLength])
|
||||
b = b[dataLength:]
|
||||
|
||||
a.hasSentHeader = true
|
||||
}
|
||||
for len(b) > 8100 {
|
||||
a.packData(buf, b[:8100])
|
||||
b = b[8100:]
|
||||
}
|
||||
if len(b) > 0 {
|
||||
a.packData(buf, b)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) DecodePacket(b []byte) ([]byte, error) { return b, nil }
|
||||
|
||||
func (a *authSHA1V4) EncodePacket(buf *bytes.Buffer, b []byte) error {
|
||||
buf.Write(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) packData(poolBuf *bytes.Buffer, data []byte) {
|
||||
dataLength := len(data)
|
||||
randDataLength := a.getRandDataLength(dataLength)
|
||||
/*
|
||||
2: uint16 BigEndian packedDataLength
|
||||
2: uint16 LittleEndian crc32Data & 0xffff
|
||||
3: maxRandDataLengthPrefix (min:1)
|
||||
4: adler32Data
|
||||
*/
|
||||
packedDataLength := 2 + 2 + 3 + randDataLength + dataLength + 4
|
||||
if randDataLength < 128 {
|
||||
packedDataLength -= 2
|
||||
}
|
||||
|
||||
binary.Write(poolBuf, binary.BigEndian, uint16(packedDataLength))
|
||||
binary.Write(poolBuf, binary.LittleEndian, uint16(crc32.ChecksumIEEE(poolBuf.Bytes()[poolBuf.Len()-2:])&0xffff))
|
||||
a.packRandData(poolBuf, randDataLength)
|
||||
poolBuf.Write(data)
|
||||
binary.Write(poolBuf, binary.LittleEndian, adler32.Checksum(poolBuf.Bytes()[poolBuf.Len()-packedDataLength+4:]))
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) packAuthData(poolBuf *bytes.Buffer, data []byte) {
|
||||
dataLength := len(data)
|
||||
randDataLength := a.getRandDataLength(12 + dataLength)
|
||||
/*
|
||||
2: uint16 BigEndian packedAuthDataLength
|
||||
4: uint32 LittleEndian crc32Data
|
||||
3: maxRandDataLengthPrefix (min: 1)
|
||||
12: authDataLength
|
||||
10: hmacSHA1DataLength
|
||||
*/
|
||||
packedAuthDataLength := 2 + 4 + 3 + randDataLength + 12 + dataLength + 10
|
||||
if randDataLength < 128 {
|
||||
packedAuthDataLength -= 2
|
||||
}
|
||||
|
||||
salt := []byte("auth_sha1_v4")
|
||||
crcData := pool.Get(len(salt) + len(a.Key) + 2)
|
||||
defer pool.Put(crcData)
|
||||
binary.BigEndian.PutUint16(crcData, uint16(packedAuthDataLength))
|
||||
copy(crcData[2:], salt)
|
||||
copy(crcData[2+len(salt):], a.Key)
|
||||
|
||||
key := pool.Get(len(a.iv) + len(a.Key))
|
||||
defer pool.Put(key)
|
||||
copy(key, a.iv)
|
||||
copy(key[len(a.iv):], a.Key)
|
||||
|
||||
poolBuf.Write(crcData[:2])
|
||||
binary.Write(poolBuf, binary.LittleEndian, crc32.ChecksumIEEE(crcData))
|
||||
a.packRandData(poolBuf, randDataLength)
|
||||
a.putAuthData(poolBuf)
|
||||
poolBuf.Write(data)
|
||||
poolBuf.Write(tools.HmacSHA1(key, poolBuf.Bytes()[poolBuf.Len()-packedAuthDataLength+10:])[:10])
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) packRandData(poolBuf *bytes.Buffer, size int) {
|
||||
if size < 128 {
|
||||
poolBuf.WriteByte(byte(size + 1))
|
||||
tools.AppendRandBytes(poolBuf, size)
|
||||
return
|
||||
}
|
||||
poolBuf.WriteByte(255)
|
||||
binary.Write(poolBuf, binary.BigEndian, uint16(size+3))
|
||||
tools.AppendRandBytes(poolBuf, size)
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) getRandDataLength(size int) int {
|
||||
if size > 1200 {
|
||||
return 0
|
||||
}
|
||||
if size > 400 {
|
||||
return rand.Intn(256)
|
||||
}
|
||||
return rand.Intn(512)
|
||||
}
|
||||
75
transport/clashssr/protocol/base.go
Normal file
75
transport/clashssr/protocol/base.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
Key []byte
|
||||
Overhead int
|
||||
Param string
|
||||
}
|
||||
|
||||
type userData struct {
|
||||
userKey []byte
|
||||
userID [4]byte
|
||||
}
|
||||
|
||||
type authData struct {
|
||||
clientID [4]byte
|
||||
connectionID uint32
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func (a *authData) next() *authData {
|
||||
r := &authData{}
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
if a.connectionID > 0xff000000 || a.connectionID == 0 {
|
||||
rand.Read(a.clientID[:])
|
||||
a.connectionID = rand.Uint32() & 0xffffff
|
||||
}
|
||||
a.connectionID++
|
||||
copy(r.clientID[:], a.clientID[:])
|
||||
r.connectionID = a.connectionID
|
||||
return r
|
||||
}
|
||||
|
||||
func (a *authData) putAuthData(buf *bytes.Buffer) {
|
||||
binary.Write(buf, binary.LittleEndian, uint32(time.Now().Unix()))
|
||||
buf.Write(a.clientID[:])
|
||||
binary.Write(buf, binary.LittleEndian, a.connectionID)
|
||||
}
|
||||
|
||||
func (a *authData) putEncryptedData(b *bytes.Buffer, userKey []byte, paddings [2]int, salt string) error {
|
||||
encrypt := pool.Get(16)
|
||||
defer pool.Put(encrypt)
|
||||
binary.LittleEndian.PutUint32(encrypt, uint32(time.Now().Unix()))
|
||||
copy(encrypt[4:], a.clientID[:])
|
||||
binary.LittleEndian.PutUint32(encrypt[8:], a.connectionID)
|
||||
binary.LittleEndian.PutUint16(encrypt[12:], uint16(paddings[0]))
|
||||
binary.LittleEndian.PutUint16(encrypt[14:], uint16(paddings[1]))
|
||||
|
||||
cipherKey := core.Kdf(base64.StdEncoding.EncodeToString(userKey)+salt, 16)
|
||||
block, err := aes.NewCipher(cipherKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iv := bytes.Repeat([]byte{0}, 16)
|
||||
cbcCipher := cipher.NewCBCEncrypter(block, iv)
|
||||
|
||||
cbcCipher.CryptBlocks(encrypt, encrypt)
|
||||
|
||||
b.Write(encrypt)
|
||||
return nil
|
||||
}
|
||||
33
transport/clashssr/protocol/origin.go
Normal file
33
transport/clashssr/protocol/origin.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
)
|
||||
|
||||
type origin struct{}
|
||||
|
||||
func init() { register("origin", newOrigin, 0) }
|
||||
|
||||
func newOrigin(b *Base) Protocol { return &origin{} }
|
||||
|
||||
func (o *origin) StreamConn(c net.Conn, iv []byte) net.Conn { return c }
|
||||
|
||||
func (o *origin) PacketConn(c net.PacketConn) net.PacketConn { return c }
|
||||
|
||||
func (o *origin) Decode(dst, src *bytes.Buffer) error {
|
||||
dst.ReadFrom(src)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *origin) Encode(buf *bytes.Buffer, b []byte) error {
|
||||
buf.Write(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *origin) DecodePacket(b []byte) ([]byte, error) { return b, nil }
|
||||
|
||||
func (o *origin) EncodePacket(buf *bytes.Buffer, b []byte) error {
|
||||
buf.Write(b)
|
||||
return nil
|
||||
}
|
||||
36
transport/clashssr/protocol/packet.go
Normal file
36
transport/clashssr/protocol/packet.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
type PacketConn struct {
|
||||
net.PacketConn
|
||||
Protocol
|
||||
}
|
||||
|
||||
func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
buf := pool.GetBuffer()
|
||||
defer pool.PutBuffer(buf)
|
||||
err := c.EncodePacket(buf, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf.Bytes(), addr)
|
||||
return len(b), err
|
||||
}
|
||||
|
||||
func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
n, addr, err := c.PacketConn.ReadFrom(b)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
decoded, err := c.DecodePacket(b[:n])
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
copy(b, decoded)
|
||||
return len(decoded), addr, nil
|
||||
}
|
||||
76
transport/clashssr/protocol/protocol.go
Normal file
76
transport/clashssr/protocol/protocol.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
errAuthSHA1V4CRC32Error = errors.New("auth_sha1_v4 decode data wrong crc32")
|
||||
errAuthSHA1V4LengthError = errors.New("auth_sha1_v4 decode data wrong length")
|
||||
errAuthSHA1V4Adler32Error = errors.New("auth_sha1_v4 decode data wrong adler32")
|
||||
errAuthAES128MACError = errors.New("auth_aes128 decode data wrong mac")
|
||||
errAuthAES128LengthError = errors.New("auth_aes128 decode data wrong length")
|
||||
errAuthAES128ChksumError = errors.New("auth_aes128 decode data wrong checksum")
|
||||
errAuthChainLengthError = errors.New("auth_chain decode data wrong length")
|
||||
errAuthChainChksumError = errors.New("auth_chain decode data wrong checksum")
|
||||
)
|
||||
|
||||
type Protocol interface {
|
||||
StreamConn(net.Conn, []byte) net.Conn
|
||||
PacketConn(net.PacketConn) net.PacketConn
|
||||
Decode(dst, src *bytes.Buffer) error
|
||||
Encode(buf *bytes.Buffer, b []byte) error
|
||||
DecodePacket([]byte) ([]byte, error)
|
||||
EncodePacket(buf *bytes.Buffer, b []byte) error
|
||||
}
|
||||
|
||||
type protocolCreator func(b *Base) Protocol
|
||||
|
||||
var protocolList = make(map[string]struct {
|
||||
overhead int
|
||||
new protocolCreator
|
||||
})
|
||||
|
||||
func register(name string, c protocolCreator, o int) {
|
||||
protocolList[name] = struct {
|
||||
overhead int
|
||||
new protocolCreator
|
||||
}{overhead: o, new: c}
|
||||
}
|
||||
|
||||
func PickProtocol(name string, b *Base) (Protocol, error) {
|
||||
if choice, ok := protocolList[name]; ok {
|
||||
b.Overhead += choice.overhead
|
||||
return choice.new(b), nil
|
||||
}
|
||||
return nil, fmt.Errorf("protocol %s not supported", name)
|
||||
}
|
||||
|
||||
func getHeadSize(b []byte, defaultValue int) int {
|
||||
if len(b) < 2 {
|
||||
return defaultValue
|
||||
}
|
||||
headType := b[0] & 7
|
||||
switch headType {
|
||||
case 1:
|
||||
return 7
|
||||
case 4:
|
||||
return 19
|
||||
case 3:
|
||||
return 4 + int(b[1])
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getDataLength(b []byte) int {
|
||||
bLength := len(b)
|
||||
dataLength := getHeadSize(b, 30) + rand.Intn(32)
|
||||
if bLength < dataLength {
|
||||
return bLength
|
||||
}
|
||||
return dataLength
|
||||
}
|
||||
50
transport/clashssr/protocol/stream.go
Normal file
50
transport/clashssr/protocol/stream.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
Protocol
|
||||
decoded bytes.Buffer
|
||||
underDecoded bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *Conn) Read(b []byte) (int, error) {
|
||||
if c.decoded.Len() > 0 {
|
||||
return c.decoded.Read(b)
|
||||
}
|
||||
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
defer pool.Put(buf)
|
||||
n, err := c.Conn.Read(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
c.underDecoded.Write(buf[:n])
|
||||
err = c.Decode(&c.decoded, &c.underDecoded)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, _ = c.decoded.Read(b)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *Conn) Write(b []byte) (int, error) {
|
||||
bLength := len(b)
|
||||
buf := pool.GetBuffer()
|
||||
defer pool.PutBuffer(buf)
|
||||
err := c.Encode(buf, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, err = c.Conn.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return bLength, nil
|
||||
}
|
||||
11
transport/clashssr/tools/bufPool.go
Normal file
11
transport/clashssr/tools/bufPool.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
)
|
||||
|
||||
func AppendRandBytes(b *bytes.Buffer, length int) {
|
||||
b.ReadFrom(io.LimitReader(rand.Reader, int64(length)))
|
||||
}
|
||||
33
transport/clashssr/tools/crypto.go
Normal file
33
transport/clashssr/tools/crypto.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
)
|
||||
|
||||
const HmacSHA1Len = 10
|
||||
|
||||
func HmacMD5(key, data []byte) []byte {
|
||||
hmacMD5 := hmac.New(md5.New, key)
|
||||
hmacMD5.Write(data)
|
||||
return hmacMD5.Sum(nil)
|
||||
}
|
||||
|
||||
func HmacSHA1(key, data []byte) []byte {
|
||||
hmacSHA1 := hmac.New(sha1.New, key)
|
||||
hmacSHA1.Write(data)
|
||||
return hmacSHA1.Sum(nil)
|
||||
}
|
||||
|
||||
func MD5Sum(b []byte) []byte {
|
||||
h := md5.New()
|
||||
h.Write(b)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func SHA1Sum(b []byte) []byte {
|
||||
h := sha1.New()
|
||||
h.Write(b)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
57
transport/clashssr/tools/random.go
Normal file
57
transport/clashssr/tools/random.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
// XorShift128Plus - a pseudorandom number generator
|
||||
type XorShift128Plus struct {
|
||||
s [2]uint64
|
||||
}
|
||||
|
||||
func (r *XorShift128Plus) Next() uint64 {
|
||||
x := r.s[0]
|
||||
y := r.s[1]
|
||||
r.s[0] = y
|
||||
x ^= x << 23
|
||||
x ^= y ^ (x >> 17) ^ (y >> 26)
|
||||
r.s[1] = x
|
||||
return x + y
|
||||
}
|
||||
|
||||
func (r *XorShift128Plus) InitFromBin(bin []byte) {
|
||||
var full []byte
|
||||
if len(bin) < 16 {
|
||||
full := pool.Get(16)[:0]
|
||||
defer pool.Put(full)
|
||||
full = append(full, bin...)
|
||||
for len(full) < 16 {
|
||||
full = append(full, 0)
|
||||
}
|
||||
} else {
|
||||
full = bin
|
||||
}
|
||||
r.s[0] = binary.LittleEndian.Uint64(full[:8])
|
||||
r.s[1] = binary.LittleEndian.Uint64(full[8:16])
|
||||
}
|
||||
|
||||
func (r *XorShift128Plus) InitFromBinAndLength(bin []byte, length int) {
|
||||
var full []byte
|
||||
if len(bin) < 16 {
|
||||
full := pool.Get(16)[:0]
|
||||
defer pool.Put(full)
|
||||
full = append(full, bin...)
|
||||
for len(full) < 16 {
|
||||
full = append(full, 0)
|
||||
}
|
||||
}
|
||||
full = bin
|
||||
binary.LittleEndian.PutUint16(full, uint16(length))
|
||||
r.s[0] = binary.LittleEndian.Uint64(full[:8])
|
||||
r.s[1] = binary.LittleEndian.Uint64(full[8:16])
|
||||
for i := 0; i < 4; i++ {
|
||||
r.Next()
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
I "github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
)
|
||||
|
||||
func NewQUICServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
return nil, I.ErrQUICNotIncluded
|
||||
}
|
||||
|
||||
func NewQUICClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
return nil, I.ErrQUICNotIncluded
|
||||
}
|
||||
|
||||
@@ -66,7 +66,9 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
return common.Close(c.conn)
|
||||
return common.Close(
|
||||
common.PtrOrNil(c.conn),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Client) connect() (*grpc.ClientConn, error) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !no_gvisor
|
||||
//go:build with_gvisor
|
||||
|
||||
package wireguard
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build no_gvisor
|
||||
//go:build !with_gvisor
|
||||
|
||||
package wireguard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user