mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-03-04 12:57:31 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b975275f5 | ||
|
|
166392fe17 | ||
|
|
5c6aa433ca | ||
|
|
2c55dc2557 | ||
|
|
56c0b088e8 | ||
|
|
5344e869a8 | ||
|
|
6cfaf15cbf | ||
|
|
31f0060b30 | ||
|
|
c60750d549 | ||
|
|
ebf5918e94 | ||
|
|
93ca18517c | ||
|
|
32d447ce99 | ||
|
|
617fef84ae | ||
|
|
d19199322d | ||
|
|
87795e3a07 | ||
|
|
85bb40aaf8 | ||
|
|
082bcec281 | ||
|
|
9283cb0f5f | ||
|
|
ae7967f662 | ||
|
|
01f8f2db2f | ||
|
|
255ff5e977 | ||
|
|
939e4109d7 | ||
|
|
40587b62b8 | ||
|
|
85e6d25de5 | ||
|
|
29a37f4f4b | ||
|
|
2f9a3b3469 | ||
|
|
40ea0ba098 | ||
|
|
8d7f947a80 | ||
|
|
71a8705636 |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -198,10 +198,11 @@ jobs:
|
||||
- name: Set variables
|
||||
run: |
|
||||
VERSION="${GITHUB_REF_NAME,,}-$(git rev-parse --short HEAD)"
|
||||
VERSION="${VERSION//\//-}"
|
||||
PackageVersion="$(curl -s "https://api.github.com/repos/MetaCubeX/mihomo/releases/latest" | jq -r '.tag_name' | sed 's/v//g' | awk -F '.' '{$NF = $NF + 1; print}' OFS='.').${VERSION/-/.}"
|
||||
if [ -n "${{ github.event.inputs.version }}" ]; then
|
||||
VERSION=${{ github.event.inputs.version }}
|
||||
PackageVersion="${VERSION#v}" >> $GITHUB_ENV
|
||||
PackageVersion="${VERSION#v}"
|
||||
fi
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||
echo "PackageVersion=${PackageVersion}" >> $GITHUB_ENV
|
||||
|
||||
3
.github/workflows/trigger-cmfa-update.yml
vendored
3
.github/workflows/trigger-cmfa-update.yml
vendored
@@ -10,9 +10,6 @@ on:
|
||||
- Alpha
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- Alpha
|
||||
|
||||
jobs:
|
||||
# Send "core-updated" to MetaCubeX/ClashMetaForAndroid to trigger update-dependencies
|
||||
|
||||
@@ -223,7 +223,7 @@ type conn struct {
|
||||
func (c *conn) RemoteDestination() string {
|
||||
if remoteAddr := c.RemoteAddr(); remoteAddr != nil {
|
||||
m := C.Metadata{}
|
||||
if err := m.SetRemoteAddr(remoteAddr); err != nil {
|
||||
if err := m.SetRemoteAddr(remoteAddr); err == nil {
|
||||
if m.Valid() {
|
||||
return m.String()
|
||||
}
|
||||
|
||||
63
common/atomic/enum.go
Normal file
63
common/atomic/enum.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Int32Enum[T ~int32] struct {
|
||||
value atomic.Int32
|
||||
}
|
||||
|
||||
func (i *Int32Enum[T]) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
func (i *Int32Enum[T]) UnmarshalJSON(b []byte) error {
|
||||
var v T
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Int32Enum[T]) MarshalYAML() (any, error) {
|
||||
return i.Load(), nil
|
||||
}
|
||||
|
||||
func (i *Int32Enum[T]) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var v T
|
||||
if err := unmarshal(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Int32Enum[T]) String() string {
|
||||
return fmt.Sprint(i.Load())
|
||||
}
|
||||
|
||||
func (i *Int32Enum[T]) Store(v T) {
|
||||
i.value.Store(int32(v))
|
||||
}
|
||||
|
||||
func (i *Int32Enum[T]) Load() T {
|
||||
return T(i.value.Load())
|
||||
}
|
||||
|
||||
func (i *Int32Enum[T]) Swap(new T) T {
|
||||
return T(i.value.Swap(int32(new)))
|
||||
}
|
||||
|
||||
func (i *Int32Enum[T]) CompareAndSwap(old, new T) bool {
|
||||
return i.value.CompareAndSwap(int32(old), int32(new))
|
||||
}
|
||||
|
||||
func NewInt32Enum[T ~int32](v T) *Int32Enum[T] {
|
||||
a := &Int32Enum[T]{}
|
||||
a.Store(v)
|
||||
return a
|
||||
}
|
||||
@@ -29,6 +29,19 @@ func (i *Bool) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Bool) MarshalYAML() (any, error) {
|
||||
return i.Load(), nil
|
||||
}
|
||||
|
||||
func (i *Bool) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var v bool
|
||||
if err := unmarshal(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Bool) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatBool(v)
|
||||
@@ -58,6 +71,19 @@ func (p *Pointer[T]) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pointer[T]) MarshalYAML() (any, error) {
|
||||
return p.Load(), nil
|
||||
}
|
||||
|
||||
func (p *Pointer[T]) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var v *T
|
||||
if err := unmarshal(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pointer[T]) String() string {
|
||||
return fmt.Sprint(p.Load())
|
||||
}
|
||||
@@ -84,6 +110,19 @@ func (i *Int32) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Int32) MarshalYAML() (any, error) {
|
||||
return i.Load(), nil
|
||||
}
|
||||
|
||||
func (i *Int32) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var v int32
|
||||
if err := unmarshal(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Int32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
@@ -111,6 +150,19 @@ func (i *Int64) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Int64) MarshalYAML() (any, error) {
|
||||
return i.Load(), nil
|
||||
}
|
||||
|
||||
func (i *Int64) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var v int64
|
||||
if err := unmarshal(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Int64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
@@ -138,6 +190,19 @@ func (i *Uint32) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Uint32) MarshalYAML() (any, error) {
|
||||
return i.Load(), nil
|
||||
}
|
||||
|
||||
func (i *Uint32) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var v uint32
|
||||
if err := unmarshal(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Uint32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
@@ -165,6 +230,19 @@ func (i *Uint64) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Uint64) MarshalYAML() (any, error) {
|
||||
return i.Load(), nil
|
||||
}
|
||||
|
||||
func (i *Uint64) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var v uint64
|
||||
if err := unmarshal(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Uint64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
@@ -192,6 +270,19 @@ func (i *Uintptr) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Uintptr) MarshalYAML() (any, error) {
|
||||
return i.Load(), nil
|
||||
}
|
||||
|
||||
func (i *Uintptr) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var v uintptr
|
||||
if err := unmarshal(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Uintptr) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
|
||||
@@ -27,11 +27,16 @@ type tValue[T any] struct {
|
||||
}
|
||||
|
||||
func (t *TypedValue[T]) Load() T {
|
||||
value, _ := t.LoadOk()
|
||||
return value
|
||||
}
|
||||
|
||||
func (t *TypedValue[T]) LoadOk() (_ T, ok bool) {
|
||||
value := t.value.Load()
|
||||
if value == nil {
|
||||
return DefaultValue[T]()
|
||||
return DefaultValue[T](), false
|
||||
}
|
||||
return value.(tValue[T]).value
|
||||
return value.(tValue[T]).value, true
|
||||
}
|
||||
|
||||
func (t *TypedValue[T]) Store(value T) {
|
||||
@@ -47,7 +52,11 @@ func (t *TypedValue[T]) Swap(new T) T {
|
||||
}
|
||||
|
||||
func (t *TypedValue[T]) CompareAndSwap(old, new T) bool {
|
||||
return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new})
|
||||
return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new}) ||
|
||||
// In the edge-case where [atomic.Value.Store] is uninitialized
|
||||
// and trying to compare with the zero value of T,
|
||||
// then compare-and-swap with the nil any value.
|
||||
(any(old) == any(DefaultValue[T]()) && t.value.CompareAndSwap(any(nil), tValue[T]{new}))
|
||||
}
|
||||
|
||||
func (t *TypedValue[T]) MarshalJSON() ([]byte, error) {
|
||||
@@ -63,6 +72,19 @@ func (t *TypedValue[T]) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TypedValue[T]) MarshalYAML() (any, error) {
|
||||
return t.Load(), nil
|
||||
}
|
||||
|
||||
func (t *TypedValue[T]) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var v T
|
||||
if err := unmarshal(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
t.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTypedValue[T any](t T) (v TypedValue[T]) {
|
||||
v.Store(t)
|
||||
return
|
||||
|
||||
77
common/atomic/value_test.go
Normal file
77
common/atomic/value_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTypedValue(t *testing.T) {
|
||||
{
|
||||
// Always wrapping should not allocate for simple values
|
||||
// because tValue[T] has the same memory layout as T.
|
||||
var v TypedValue[bool]
|
||||
bools := []bool{true, false}
|
||||
if n := int(testing.AllocsPerRun(1000, func() {
|
||||
for _, b := range bools {
|
||||
v.Store(b)
|
||||
}
|
||||
})); n != 0 {
|
||||
t.Errorf("AllocsPerRun = %d, want 0", n)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var v TypedValue[int]
|
||||
got, gotOk := v.LoadOk()
|
||||
if got != 0 || gotOk {
|
||||
t.Fatalf("LoadOk = (%v, %v), want (0, false)", got, gotOk)
|
||||
}
|
||||
v.Store(1)
|
||||
got, gotOk = v.LoadOk()
|
||||
if got != 1 || !gotOk {
|
||||
t.Fatalf("LoadOk = (%v, %v), want (1, true)", got, gotOk)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var v TypedValue[error]
|
||||
got, gotOk := v.LoadOk()
|
||||
if got != nil || gotOk {
|
||||
t.Fatalf("LoadOk = (%v, %v), want (nil, false)", got, gotOk)
|
||||
}
|
||||
v.Store(io.EOF)
|
||||
got, gotOk = v.LoadOk()
|
||||
if got != io.EOF || !gotOk {
|
||||
t.Fatalf("LoadOk = (%v, %v), want (EOF, true)", got, gotOk)
|
||||
}
|
||||
err := &os.PathError{}
|
||||
v.Store(err)
|
||||
got, gotOk = v.LoadOk()
|
||||
if got != err || !gotOk {
|
||||
t.Fatalf("LoadOk = (%v, %v), want (%v, true)", got, gotOk, err)
|
||||
}
|
||||
v.Store(nil)
|
||||
got, gotOk = v.LoadOk()
|
||||
if got != nil || !gotOk {
|
||||
t.Fatalf("LoadOk = (%v, %v), want (nil, true)", got, gotOk)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
c1, c2, c3 := make(chan struct{}), make(chan struct{}), make(chan struct{})
|
||||
var v TypedValue[chan struct{}]
|
||||
if v.CompareAndSwap(c1, c2) != false {
|
||||
t.Fatalf("CompareAndSwap = true, want false")
|
||||
}
|
||||
if v.CompareAndSwap(nil, c1) != true {
|
||||
t.Fatalf("CompareAndSwap = false, want true")
|
||||
}
|
||||
if v.CompareAndSwap(c2, c3) != false {
|
||||
t.Fatalf("CompareAndSwap = true, want false")
|
||||
}
|
||||
if v.CompareAndSwap(c1, c2) != true {
|
||||
t.Fatalf("CompareAndSwap = false, want true")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package convert
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -43,3 +44,22 @@ func decodeUrlSafe(data string) string {
|
||||
}
|
||||
return string(dcBuf)
|
||||
}
|
||||
|
||||
func TryDecodeBase64(s string) (decoded []byte, err error) {
|
||||
if len(s)%4 == 0 {
|
||||
if decoded, err = base64.StdEncoding.DecodeString(s); err == nil {
|
||||
return
|
||||
}
|
||||
if decoded, err = base64.URLEncoding.DecodeString(s); err == nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if decoded, err = base64.RawStdEncoding.DecodeString(s); err == nil {
|
||||
return
|
||||
}
|
||||
if decoded, err = base64.RawURLEncoding.DecodeString(s); err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("invalid base64-encoded string")
|
||||
}
|
||||
|
||||
@@ -456,12 +456,12 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||
proxies = append(proxies, ss)
|
||||
|
||||
case "ssr":
|
||||
dcBuf, err := encRaw.DecodeString(body)
|
||||
dcBuf, err := TryDecodeBase64(body)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// ssr://host:port:protocol:method:obfs:urlsafebase64pass/?obfsparam=urlsafebase64&protoparam=&remarks=urlsafebase64&group=urlsafebase64&udpport=0&uot=1
|
||||
// ssr://host:port:protocol:method:obfs:urlsafebase64pass/?obfsparam=urlsafebase64param&protoparam=urlsafebase64param&remarks=urlsafebase64remarks&group=urlsafebase64group&udpport=0&uot=1
|
||||
|
||||
before, after, ok := strings.Cut(string(dcBuf), "/?")
|
||||
if !ok {
|
||||
@@ -490,7 +490,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||
name := uniqueName(names, remarks)
|
||||
|
||||
obfsParam := decodeUrlSafe(query.Get("obfsparam"))
|
||||
protocolParam := query.Get("protoparam")
|
||||
protocolParam := decodeUrlSafe(query.Get("protoparam"))
|
||||
|
||||
ssr := make(map[string]any, 20)
|
||||
|
||||
@@ -513,6 +513,101 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||
}
|
||||
|
||||
proxies = append(proxies, ssr)
|
||||
|
||||
case "socks", "socks5", "socks5h", "http", "https":
|
||||
link, err := url.Parse(line)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
server := link.Hostname()
|
||||
if server == "" {
|
||||
continue
|
||||
}
|
||||
portStr := link.Port()
|
||||
if portStr == "" {
|
||||
continue
|
||||
}
|
||||
remarks := link.Fragment
|
||||
if remarks == "" {
|
||||
remarks = fmt.Sprintf("%s:%s", server, portStr)
|
||||
}
|
||||
name := uniqueName(names, remarks)
|
||||
encodeStr := link.User.String()
|
||||
var username, password string
|
||||
if encodeStr != "" {
|
||||
decodeStr := string(DecodeBase64([]byte(encodeStr)))
|
||||
splitStr := strings.Split(decodeStr, ":")
|
||||
|
||||
// todo: should use url.QueryUnescape ?
|
||||
username = splitStr[0]
|
||||
if len(splitStr) == 2 {
|
||||
password = splitStr[1]
|
||||
}
|
||||
}
|
||||
socks := make(map[string]any, 10)
|
||||
socks["name"] = name
|
||||
socks["type"] = func() string {
|
||||
switch scheme {
|
||||
case "socks", "socks5", "socks5h":
|
||||
return "socks5"
|
||||
case "http", "https":
|
||||
return "http"
|
||||
}
|
||||
return scheme
|
||||
}()
|
||||
socks["server"] = server
|
||||
socks["port"] = portStr
|
||||
socks["username"] = username
|
||||
socks["password"] = password
|
||||
socks["skip-cert-verify"] = true
|
||||
if scheme == "https" {
|
||||
socks["tls"] = true
|
||||
}
|
||||
|
||||
proxies = append(proxies, socks)
|
||||
|
||||
case "anytls":
|
||||
// https://github.com/anytls/anytls-go/blob/main/docs/uri_scheme.md
|
||||
link, err := url.Parse(line)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
username := link.User.Username()
|
||||
password, exist := link.User.Password()
|
||||
if !exist {
|
||||
password = username
|
||||
}
|
||||
query := link.Query()
|
||||
server := link.Hostname()
|
||||
if server == "" {
|
||||
continue
|
||||
}
|
||||
portStr := link.Port()
|
||||
if portStr == "" {
|
||||
continue
|
||||
}
|
||||
insecure, sni := query.Get("insecure"), query.Get("sni")
|
||||
insecureBool := insecure == "1"
|
||||
fingerprint := query.Get("hpkp")
|
||||
|
||||
remarks := link.Fragment
|
||||
if remarks == "" {
|
||||
remarks = fmt.Sprintf("%s:%s", server, portStr)
|
||||
}
|
||||
name := uniqueName(names, remarks)
|
||||
anytls := make(map[string]any, 10)
|
||||
anytls["name"] = name
|
||||
anytls["type"] = "anytls"
|
||||
anytls["server"] = server
|
||||
anytls["port"] = portStr
|
||||
anytls["username"] = username
|
||||
anytls["password"] = password
|
||||
anytls["sni"] = sni
|
||||
anytls["fingerprint"] = fingerprint
|
||||
anytls["skip-cert-verify"] = insecureBool
|
||||
anytls["udp"] = true
|
||||
|
||||
proxies = append(proxies, anytls)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,18 +8,23 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var defaultAllocator = NewAllocator()
|
||||
var DefaultAllocator = NewAllocator()
|
||||
|
||||
// Allocator for incoming frames, optimized to prevent overwriting after zeroing
|
||||
type Allocator struct {
|
||||
type Allocator interface {
|
||||
Get(size int) []byte
|
||||
Put(buf []byte) error
|
||||
}
|
||||
|
||||
// defaultAllocator for incoming frames, optimized to prevent overwriting after zeroing
|
||||
type defaultAllocator struct {
|
||||
buffers [11]sync.Pool
|
||||
}
|
||||
|
||||
// NewAllocator initiates a []byte allocator for frames less than 65536 bytes,
|
||||
// the waste(memory fragmentation) of space allocation is guaranteed to be
|
||||
// no more than 50%.
|
||||
func NewAllocator() *Allocator {
|
||||
return &Allocator{
|
||||
func NewAllocator() Allocator {
|
||||
return &defaultAllocator{
|
||||
buffers: [...]sync.Pool{ // 64B -> 64K
|
||||
{New: func() any { return new([1 << 6]byte) }},
|
||||
{New: func() any { return new([1 << 7]byte) }},
|
||||
@@ -37,7 +42,7 @@ func NewAllocator() *Allocator {
|
||||
}
|
||||
|
||||
// Get a []byte from pool with most appropriate cap
|
||||
func (alloc *Allocator) Get(size int) []byte {
|
||||
func (alloc *defaultAllocator) Get(size int) []byte {
|
||||
switch {
|
||||
case size < 0:
|
||||
panic("alloc.Get: len out of range")
|
||||
@@ -87,7 +92,7 @@ func (alloc *Allocator) Get(size int) []byte {
|
||||
|
||||
// Put returns a []byte to pool for future use,
|
||||
// which the cap must be exactly 2^n
|
||||
func (alloc *Allocator) Put(buf []byte) error {
|
||||
func (alloc *defaultAllocator) Put(buf []byte) error {
|
||||
if cap(buf) == 0 || cap(buf) > 65536 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
package pool
|
||||
|
||||
const (
|
||||
// RelayBufferSize using for tcp
|
||||
// io.Copy default buffer size is 32 KiB
|
||||
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
||||
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
||||
RelayBufferSize = 16 * 1024
|
||||
|
||||
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
|
||||
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
|
||||
// UDPBufferSize using for udp
|
||||
// Most UDPs are smaller than the MTU, and the TUN's MTU
|
||||
// set to 9000, so the UDP Buffer size set to 16Kib
|
||||
UDPBufferSize = 8 * 1024
|
||||
)
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
package pool
|
||||
|
||||
const (
|
||||
// RelayBufferSize using for tcp
|
||||
// io.Copy default buffer size is 32 KiB
|
||||
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
||||
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
||||
RelayBufferSize = 20 * 1024
|
||||
RelayBufferSize = 32 * 1024
|
||||
|
||||
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
|
||||
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
|
||||
// UDPBufferSize using for udp
|
||||
// Most UDPs are smaller than the MTU, and the TUN's MTU
|
||||
// set to 9000, so the UDP Buffer size set to 16Kib
|
||||
UDPBufferSize = 16 * 1024
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package pool
|
||||
|
||||
func Get(size int) []byte {
|
||||
return defaultAllocator.Get(size)
|
||||
return DefaultAllocator.Get(size)
|
||||
}
|
||||
|
||||
func Put(buf []byte) error {
|
||||
return defaultAllocator.Put(buf)
|
||||
return DefaultAllocator.Put(buf)
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ package pool
|
||||
import "github.com/metacubex/sing/common/buf"
|
||||
|
||||
func init() {
|
||||
buf.DefaultAllocator = defaultAllocator
|
||||
buf.DefaultAllocator = DefaultAllocator
|
||||
}
|
||||
|
||||
@@ -239,13 +239,15 @@ func (n *num) UnmarshalText(text []byte) (err error) {
|
||||
|
||||
func TestStructure_TextUnmarshaller(t *testing.T) {
|
||||
rawMap := map[string]any{
|
||||
"num": "255",
|
||||
"num_p": "127",
|
||||
"num": "255",
|
||||
"num_p": "127",
|
||||
"num_arr": []string{"1", "2", "3"},
|
||||
}
|
||||
|
||||
s := &struct {
|
||||
Num num `test:"num"`
|
||||
NumP *num `test:"num_p"`
|
||||
Num num `test:"num"`
|
||||
NumP *num `test:"num_p"`
|
||||
NumArr []num `test:"num_arr"`
|
||||
}{}
|
||||
|
||||
err := decoder.Decode(rawMap, s)
|
||||
@@ -253,6 +255,7 @@ func TestStructure_TextUnmarshaller(t *testing.T) {
|
||||
assert.Equal(t, 255, s.Num.a)
|
||||
assert.NotNil(t, s.NumP)
|
||||
assert.Equal(t, s.NumP.a, 127)
|
||||
assert.Equal(t, s.NumArr, []num{{1}, {2}, {3}})
|
||||
|
||||
// test WeaklyTypedInput
|
||||
rawMap["num"] = 256
|
||||
|
||||
@@ -41,3 +41,11 @@ func NewAuthenticator(users []AuthUser) Authenticator {
|
||||
}
|
||||
return au
|
||||
}
|
||||
|
||||
var AlwaysValid Authenticator = alwaysValid{}
|
||||
|
||||
type alwaysValid struct{}
|
||||
|
||||
func (alwaysValid) Verify(string, string) bool { return true }
|
||||
|
||||
func (alwaysValid) Users() []string { return nil }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package geodata
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -76,13 +75,13 @@ func LoadGeoSiteMatcher(countryCode string) (router.DomainMatcher, error) {
|
||||
if countryCode[0] == '!' {
|
||||
not = true
|
||||
countryCode = countryCode[1:]
|
||||
if countryCode == "" {
|
||||
return nil, fmt.Errorf("country code could not be empty")
|
||||
}
|
||||
}
|
||||
countryCode = strings.ToLower(countryCode)
|
||||
|
||||
parts := strings.Split(countryCode, "@")
|
||||
if len(parts) == 0 {
|
||||
return nil, errors.New("empty rule")
|
||||
}
|
||||
listName := strings.TrimSpace(parts[0])
|
||||
attrVal := parts[1:]
|
||||
attrs := parseAttrs(attrVal)
|
||||
|
||||
@@ -1,57 +1,52 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
FindProcessAlways = "always"
|
||||
FindProcessStrict = "strict"
|
||||
FindProcessOff = "off"
|
||||
FindProcessStrict FindProcessMode = iota
|
||||
FindProcessAlways
|
||||
FindProcessOff
|
||||
)
|
||||
|
||||
var (
|
||||
validModes = map[string]struct{}{
|
||||
FindProcessAlways: {},
|
||||
FindProcessOff: {},
|
||||
FindProcessStrict: {},
|
||||
validModes = map[string]FindProcessMode{
|
||||
FindProcessStrict.String(): FindProcessStrict,
|
||||
FindProcessAlways.String(): FindProcessAlways,
|
||||
FindProcessOff.String(): FindProcessOff,
|
||||
}
|
||||
)
|
||||
|
||||
type FindProcessMode string
|
||||
type FindProcessMode int32
|
||||
|
||||
func (m FindProcessMode) Always() bool {
|
||||
return m == FindProcessAlways
|
||||
}
|
||||
|
||||
func (m FindProcessMode) Off() bool {
|
||||
return m == FindProcessOff
|
||||
}
|
||||
|
||||
func (m *FindProcessMode) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var tp string
|
||||
if err := unmarshal(&tp); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.Set(tp)
|
||||
}
|
||||
|
||||
func (m *FindProcessMode) UnmarshalJSON(data []byte) error {
|
||||
var tp string
|
||||
if err := json.Unmarshal(data, &tp); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.Set(tp)
|
||||
// UnmarshalText unserialize FindProcessMode
|
||||
func (m *FindProcessMode) UnmarshalText(data []byte) error {
|
||||
return m.Set(string(data))
|
||||
}
|
||||
|
||||
func (m *FindProcessMode) Set(value string) error {
|
||||
mode := strings.ToLower(value)
|
||||
_, exist := validModes[mode]
|
||||
mode, exist := validModes[strings.ToLower(value)]
|
||||
if !exist {
|
||||
return errors.New("invalid find process mode")
|
||||
}
|
||||
*m = FindProcessMode(mode)
|
||||
*m = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText serialize FindProcessMode
|
||||
func (m FindProcessMode) MarshalText() ([]byte, error) {
|
||||
return []byte(m.String()), nil
|
||||
}
|
||||
|
||||
func (m FindProcessMode) String() string {
|
||||
switch m {
|
||||
case FindProcessAlways:
|
||||
return "always"
|
||||
case FindProcessOff:
|
||||
return "off"
|
||||
default:
|
||||
return "strict"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,17 +46,24 @@ func RelayDnsConn(ctx context.Context, conn net.Conn, readTimeout time.Duration)
|
||||
ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout)
|
||||
defer cancel()
|
||||
inData := buff[:n]
|
||||
msg, err := relayDnsPacket(ctx, inData, buff, 0)
|
||||
outBuff := buff[2:]
|
||||
msg, err := relayDnsPacket(ctx, inData, outBuff, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = binary.Write(conn, binary.BigEndian, uint16(len(msg)))
|
||||
if err != nil {
|
||||
return err
|
||||
if &msg[0] == &outBuff[0] { // msg is still in the buff
|
||||
binary.BigEndian.PutUint16(buff[:2], uint16(len(msg)))
|
||||
outBuff = buff[:2+len(msg)]
|
||||
} else { // buff not big enough (WTF???)
|
||||
newBuff := pool.Get(len(msg) + 2)
|
||||
defer pool.Put(newBuff)
|
||||
binary.BigEndian.PutUint16(newBuff[:2], uint16(len(msg)))
|
||||
copy(newBuff[2:], msg)
|
||||
outBuff = newBuff
|
||||
}
|
||||
|
||||
_, err = conn.Write(msg)
|
||||
_, err = conn.Write(outBuff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/sing/common/metadata"
|
||||
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
@@ -164,6 +166,9 @@ func replaceDomain(metadata *C.Metadata, host string, overrideDest bool) {
|
||||
}
|
||||
|
||||
func (sd *Dispatcher) domainCanReplace(host string) bool {
|
||||
if host == "." || !metadata.IsDomainName(host) {
|
||||
return false
|
||||
}
|
||||
for _, matcher := range sd.skipDomain {
|
||||
if matcher.MatchDomain(host) {
|
||||
return false
|
||||
|
||||
@@ -270,6 +270,7 @@ type RawTun struct {
|
||||
AutoRedirect bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"`
|
||||
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"`
|
||||
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"`
|
||||
LoopbackAddress []netip.Addr `yaml:"loopback-address" json:"loopback-address,omitempty"`
|
||||
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
RouteAddress []netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
|
||||
RouteAddressSet []string `yaml:"route-address-set" json:"route-address-set,omitempty"`
|
||||
@@ -1168,10 +1169,19 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.
|
||||
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
|
||||
}
|
||||
|
||||
proxyName := u.Fragment
|
||||
var proxyName string
|
||||
params := map[string]string{}
|
||||
for _, s := range strings.Split(u.Fragment, "&") {
|
||||
arr := strings.SplitN(s, "=", 2)
|
||||
switch len(arr) {
|
||||
case 1:
|
||||
proxyName = arr[0]
|
||||
case 2:
|
||||
params[arr[0]] = arr[1]
|
||||
}
|
||||
}
|
||||
|
||||
var addr, dnsNetType string
|
||||
params := map[string]string{}
|
||||
switch u.Scheme {
|
||||
case "udp":
|
||||
addr, err = hostWithDefaultPort(u.Host, "53")
|
||||
@@ -1189,23 +1199,8 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.
|
||||
addr, err = hostWithDefaultPort(u.Host, "80")
|
||||
}
|
||||
if err == nil {
|
||||
proxyName = ""
|
||||
clearURL := url.URL{Scheme: u.Scheme, Host: addr, Path: u.Path, User: u.User}
|
||||
addr = clearURL.String()
|
||||
if len(u.Fragment) != 0 {
|
||||
for _, s := range strings.Split(u.Fragment, "&") {
|
||||
arr := strings.Split(s, "=")
|
||||
if len(arr) == 0 {
|
||||
continue
|
||||
} else if len(arr) == 1 {
|
||||
proxyName = arr[0]
|
||||
} else if len(arr) == 2 {
|
||||
params[arr[0]] = arr[1]
|
||||
} else {
|
||||
params[arr[0]] = strings.Join(arr[1:], "=")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "quic":
|
||||
addr, err = hostWithDefaultPort(u.Host, "853")
|
||||
@@ -1563,6 +1558,7 @@ func parseTun(rawTun RawTun, general *General) error {
|
||||
AutoRedirect: rawTun.AutoRedirect,
|
||||
AutoRedirectInputMark: rawTun.AutoRedirectInputMark,
|
||||
AutoRedirectOutputMark: rawTun.AutoRedirectOutputMark,
|
||||
LoopbackAddress: rawTun.LoopbackAddress,
|
||||
StrictRoute: rawTun.StrictRoute,
|
||||
RouteAddress: rawTun.RouteAddress,
|
||||
RouteAddressSet: rawTun.RouteAddressSet,
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
||||
@@ -150,6 +152,9 @@ func proxyGroupsDagSort(groupsConfig []map[string]any) error {
|
||||
}
|
||||
|
||||
func verifyIP6() bool {
|
||||
if skip, _ := strconv.ParseBool(os.Getenv("SKIP_SYSTEM_IPV6_CHECK")); skip {
|
||||
return true
|
||||
}
|
||||
if iAddrs, err := net.InterfaceAddrs(); err == nil {
|
||||
for _, addr := range iAddrs {
|
||||
if prefix, err := netip.ParsePrefix(addr.String()); err == nil {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package constant
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
@@ -22,44 +21,6 @@ const (
|
||||
|
||||
type DNSMode int
|
||||
|
||||
// UnmarshalYAML unserialize EnhancedMode with yaml
|
||||
func (e *DNSMode) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var tp string
|
||||
if err := unmarshal(&tp); err != nil {
|
||||
return err
|
||||
}
|
||||
mode, exist := DNSModeMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid mode")
|
||||
}
|
||||
*e = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML serialize EnhancedMode with yaml
|
||||
func (e DNSMode) MarshalYAML() (any, error) {
|
||||
return e.String(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON unserialize EnhancedMode with json
|
||||
func (e *DNSMode) UnmarshalJSON(data []byte) error {
|
||||
var tp string
|
||||
if err := json.Unmarshal(data, &tp); err != nil {
|
||||
return err
|
||||
}
|
||||
mode, exist := DNSModeMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid mode")
|
||||
}
|
||||
*e = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON serialize EnhancedMode with json
|
||||
func (e DNSMode) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(e.String())
|
||||
}
|
||||
|
||||
// UnmarshalText unserialize EnhancedMode
|
||||
func (e *DNSMode) UnmarshalText(data []byte) error {
|
||||
mode, exist := DNSModeMapping[strings.ToLower(string(data))]
|
||||
@@ -157,40 +118,6 @@ func (e FilterMode) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (e FilterMode) MarshalYAML() (interface{}, error) {
|
||||
return e.String(), nil
|
||||
}
|
||||
|
||||
func (e *FilterMode) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var tp string
|
||||
if err := unmarshal(&tp); err != nil {
|
||||
return err
|
||||
}
|
||||
mode, exist := FilterModeMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid mode")
|
||||
}
|
||||
*e = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e FilterMode) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(e.String())
|
||||
}
|
||||
|
||||
func (e *FilterMode) UnmarshalJSON(data []byte) error {
|
||||
var tp string
|
||||
if err := json.Unmarshal(data, &tp); err != nil {
|
||||
return err
|
||||
}
|
||||
mode, exist := FilterModeMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid mode")
|
||||
}
|
||||
*e = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e FilterMode) MarshalText() ([]byte, error) {
|
||||
return []byte(e.String()), nil
|
||||
}
|
||||
|
||||
@@ -91,9 +91,7 @@ type RuleProvider interface {
|
||||
Provider
|
||||
Behavior() RuleBehavior
|
||||
Count() int
|
||||
Match(*constant.Metadata) bool
|
||||
ShouldResolveIP() bool
|
||||
ShouldFindProcess() bool
|
||||
Match(metadata *constant.Metadata, helper constant.RuleMatchHelper) bool
|
||||
Strategy() any
|
||||
}
|
||||
|
||||
|
||||
@@ -111,14 +111,17 @@ func (rt RuleType) String() string {
|
||||
|
||||
type Rule interface {
|
||||
RuleType() RuleType
|
||||
Match(metadata *Metadata) (bool, string)
|
||||
Match(metadata *Metadata, helper RuleMatchHelper) (bool, string)
|
||||
Adapter() string
|
||||
Payload() string
|
||||
ShouldResolveIP() bool
|
||||
ShouldFindProcess() bool
|
||||
ProviderNames() []string
|
||||
}
|
||||
|
||||
type RuleMatchHelper struct {
|
||||
ResolveIP func()
|
||||
FindProcess func()
|
||||
}
|
||||
|
||||
type RuleGroup interface {
|
||||
Rule
|
||||
GetRecodeSize() int
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package constant
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
@@ -20,42 +19,6 @@ const (
|
||||
|
||||
type TUNStack int
|
||||
|
||||
// UnmarshalYAML unserialize TUNStack with yaml
|
||||
func (e *TUNStack) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var tp string
|
||||
if err := unmarshal(&tp); err != nil {
|
||||
return err
|
||||
}
|
||||
mode, exist := StackTypeMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid tun stack")
|
||||
}
|
||||
*e = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML serialize TUNStack with yaml
|
||||
func (e TUNStack) MarshalYAML() (any, error) {
|
||||
return e.String(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON unserialize TUNStack with json
|
||||
func (e *TUNStack) UnmarshalJSON(data []byte) error {
|
||||
var tp string
|
||||
json.Unmarshal(data, &tp)
|
||||
mode, exist := StackTypeMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid tun stack")
|
||||
}
|
||||
*e = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON serialize TUNStack with json
|
||||
func (e TUNStack) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(e.String())
|
||||
}
|
||||
|
||||
// UnmarshalText unserialize TUNStack
|
||||
func (e *TUNStack) UnmarshalText(data []byte) error {
|
||||
mode, exist := StackTypeMapping[strings.ToLower(string(data))]
|
||||
|
||||
@@ -6,8 +6,10 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
@@ -105,3 +107,24 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
|
||||
}
|
||||
|
||||
func (c *client) ResetConnection() {}
|
||||
|
||||
func newClient(addr string, resolver *Resolver, netType string, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) *client {
|
||||
host, port, _ := net.SplitHostPort(addr)
|
||||
c := &client{
|
||||
Client: &D.Client{
|
||||
Net: netType,
|
||||
TLSConfig: &tls.Config{
|
||||
ServerName: host,
|
||||
},
|
||||
UDPSize: 4096,
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
port: port,
|
||||
host: host,
|
||||
dialer: newDNSDialer(resolver, proxyAdapter, proxyName),
|
||||
}
|
||||
if params["skip-cert-verify"] == "true" {
|
||||
c.TLSConfig.InsecureSkipVerify = true
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
33
dns/doh.go
33
dns/doh.go
@@ -9,7 +9,6 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -71,8 +70,6 @@ type dnsOverHTTPS struct {
|
||||
dialer *dnsDialer
|
||||
addr string
|
||||
skipCertVerify bool
|
||||
ecsPrefix netip.Prefix
|
||||
ecsOverride bool
|
||||
}
|
||||
|
||||
// type check
|
||||
@@ -105,28 +102,6 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin
|
||||
doh.skipCertVerify = true
|
||||
}
|
||||
|
||||
if ecs := params["ecs"]; ecs != "" {
|
||||
prefix, err := netip.ParsePrefix(ecs)
|
||||
if err != nil {
|
||||
addr, err := netip.ParseAddr(ecs)
|
||||
if err != nil {
|
||||
log.Warnln("DOH [%s] config with invalid ecs: %s", doh.addr, ecs)
|
||||
} else {
|
||||
doh.ecsPrefix = netip.PrefixFrom(addr, addr.BitLen())
|
||||
}
|
||||
} else {
|
||||
doh.ecsPrefix = prefix
|
||||
}
|
||||
}
|
||||
|
||||
if doh.ecsPrefix.IsValid() {
|
||||
log.Debugln("DOH [%s] config with ecs: %s", doh.addr, doh.ecsPrefix)
|
||||
}
|
||||
|
||||
if params["ecs-override"] == "true" {
|
||||
doh.ecsOverride = true
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(doh, (*dnsOverHTTPS).Close)
|
||||
|
||||
return doh
|
||||
@@ -154,10 +129,6 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.
|
||||
}
|
||||
}()
|
||||
|
||||
if doh.ecsPrefix.IsValid() {
|
||||
setEdns0Subnet(m, doh.ecsPrefix, doh.ecsOverride)
|
||||
}
|
||||
|
||||
// Check if there was already an active client before sending the request.
|
||||
// We'll only attempt to re-connect if there was one.
|
||||
client, isCached, err := doh.getClient(ctx)
|
||||
@@ -552,8 +523,8 @@ func (doh *dnsOverHTTPS) createTransportH3(
|
||||
Dial: func(
|
||||
ctx context.Context,
|
||||
|
||||
// Ignore the address and always connect to the one that we got
|
||||
// from the bootstrapper.
|
||||
// Ignore the address and always connect to the one that we got
|
||||
// from the bootstrapper.
|
||||
_ string,
|
||||
tlsCfg *tlsC.Config,
|
||||
cfg *quic.Config,
|
||||
|
||||
15
dns/doq.go
15
dns/doq.go
@@ -61,15 +61,16 @@ type dnsOverQUIC struct {
|
||||
bytesPool *sync.Pool
|
||||
bytesPoolGuard sync.Mutex
|
||||
|
||||
addr string
|
||||
dialer *dnsDialer
|
||||
addr string
|
||||
dialer *dnsDialer
|
||||
skipCertVerify bool
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ dnsClient = (*dnsOverQUIC)(nil)
|
||||
|
||||
// newDoQ returns the DNS-over-QUIC Upstream.
|
||||
func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) {
|
||||
func newDoQ(addr string, resolver *Resolver, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) *dnsOverQUIC {
|
||||
doq := &dnsOverQUIC{
|
||||
addr: addr,
|
||||
dialer: newDNSDialer(resolver, proxyAdapter, proxyName),
|
||||
@@ -79,8 +80,12 @@ func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyN
|
||||
},
|
||||
}
|
||||
|
||||
if params["skip-cert-verify"] == "true" {
|
||||
doq.skipCertVerify = true
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(doq, (*dnsOverQUIC).Close)
|
||||
return doq, nil
|
||||
return doq
|
||||
}
|
||||
|
||||
// Address implements the Upstream interface for *dnsOverQUIC.
|
||||
@@ -329,7 +334,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
|
||||
tlsConfig := ca.GetGlobalTLSConfig(
|
||||
&tls.Config{
|
||||
ServerName: host,
|
||||
InsecureSkipVerify: false,
|
||||
InsecureSkipVerify: doq.skipCertVerify,
|
||||
NextProtos: []string{
|
||||
NextProtoDQ,
|
||||
},
|
||||
|
||||
107
dns/util.go
107
dns/util.go
@@ -2,10 +2,8 @@ package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -92,46 +90,95 @@ func isIPRequest(q D.Question) bool {
|
||||
func transform(servers []NameServer, resolver *Resolver) []dnsClient {
|
||||
ret := make([]dnsClient, 0, len(servers))
|
||||
for _, s := range servers {
|
||||
var c dnsClient
|
||||
switch s.Net {
|
||||
case "https":
|
||||
ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName))
|
||||
continue
|
||||
c = newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName)
|
||||
case "dhcp":
|
||||
ret = append(ret, newDHCPClient(s.Addr))
|
||||
continue
|
||||
c = newDHCPClient(s.Addr)
|
||||
case "system":
|
||||
ret = append(ret, newSystemClient())
|
||||
continue
|
||||
c = newSystemClient()
|
||||
case "rcode":
|
||||
ret = append(ret, newRCodeClient(s.Addr))
|
||||
continue
|
||||
c = newRCodeClient(s.Addr)
|
||||
case "quic":
|
||||
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter, s.ProxyName); err == nil {
|
||||
ret = append(ret, doq)
|
||||
} else {
|
||||
log.Fatalln("DoQ format error: %v", err)
|
||||
}
|
||||
continue
|
||||
c = newDoQ(s.Addr, resolver, s.Params, s.ProxyAdapter, s.ProxyName)
|
||||
default:
|
||||
c = newClient(s.Addr, resolver, s.Net, s.Params, s.ProxyAdapter, s.ProxyName)
|
||||
}
|
||||
|
||||
host, port, _ := net.SplitHostPort(s.Addr)
|
||||
ret = append(ret, &client{
|
||||
Client: &D.Client{
|
||||
Net: s.Net,
|
||||
TLSConfig: &tls.Config{
|
||||
ServerName: host,
|
||||
},
|
||||
UDPSize: 4096,
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
port: port,
|
||||
host: host,
|
||||
dialer: newDNSDialer(resolver, s.ProxyAdapter, s.ProxyName),
|
||||
})
|
||||
c = warpClientWithEdns0Subnet(c, s.Params)
|
||||
|
||||
if s.Params["disable-ipv4"] == "true" {
|
||||
c = warpClientWithDisableType(c, D.TypeA)
|
||||
}
|
||||
|
||||
if s.Params["disable-ipv6"] == "true" {
|
||||
c = warpClientWithDisableType(c, D.TypeAAAA)
|
||||
}
|
||||
|
||||
ret = append(ret, c)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
type clientWithDisableType struct {
|
||||
dnsClient
|
||||
qType uint16
|
||||
}
|
||||
|
||||
func (c clientWithDisableType) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
|
||||
if len(m.Question) > 0 {
|
||||
q := m.Question[0]
|
||||
if q.Qtype == c.qType {
|
||||
return handleMsgWithEmptyAnswer(m), nil
|
||||
}
|
||||
}
|
||||
return c.dnsClient.ExchangeContext(ctx, m)
|
||||
}
|
||||
|
||||
func warpClientWithDisableType(c dnsClient, qType uint16) dnsClient {
|
||||
return clientWithDisableType{c, qType}
|
||||
}
|
||||
|
||||
type clientWithEdns0Subnet struct {
|
||||
dnsClient
|
||||
ecsPrefix netip.Prefix
|
||||
ecsOverride bool
|
||||
}
|
||||
|
||||
func (c clientWithEdns0Subnet) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {
|
||||
m = m.Copy()
|
||||
setEdns0Subnet(m, c.ecsPrefix, c.ecsOverride)
|
||||
return c.dnsClient.ExchangeContext(ctx, m)
|
||||
}
|
||||
|
||||
func warpClientWithEdns0Subnet(c dnsClient, params map[string]string) dnsClient {
|
||||
var ecsPrefix netip.Prefix
|
||||
var ecsOverride bool
|
||||
if ecs := params["ecs"]; ecs != "" {
|
||||
prefix, err := netip.ParsePrefix(ecs)
|
||||
if err != nil {
|
||||
addr, err := netip.ParseAddr(ecs)
|
||||
if err != nil {
|
||||
log.Warnln("DNS [%s] config with invalid ecs: %s", c.Address(), ecs)
|
||||
} else {
|
||||
ecsPrefix = netip.PrefixFrom(addr, addr.BitLen())
|
||||
}
|
||||
} else {
|
||||
ecsPrefix = prefix
|
||||
}
|
||||
}
|
||||
|
||||
if ecsPrefix.IsValid() {
|
||||
log.Debugln("DNS [%s] config with ecs: %s", c.Address(), ecsPrefix)
|
||||
if params["ecs-override"] == "true" {
|
||||
ecsOverride = true
|
||||
}
|
||||
return clientWithEdns0Subnet{c, ecsPrefix, ecsOverride}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func handleMsgWithEmptyAnswer(r *D.Msg) *D.Msg {
|
||||
msg := &D.Msg{}
|
||||
msg.Answer = []D.RR{}
|
||||
|
||||
@@ -1020,7 +1020,7 @@ proxy-providers:
|
||||
type: http # http 的 path 可空置,默认储存路径为 homedir 的 proxies 文件夹,文件名为 url 的 md5
|
||||
url: "url"
|
||||
interval: 3600
|
||||
path: ./provider1.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到任意位置,添加环境变量 SKIP_SAFE_PATH_CHECK=1
|
||||
path: ./provider1.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到其他位置,请通过设置 SAFE_PATHS 环境变量指定额外的安全路径。该环境变量的语法同本操作系统的PATH环境变量解析规则(即Windows下以分号分割,其他系统下以冒号分割)
|
||||
proxy: DIRECT
|
||||
# size-limit: 10240 # 限制下载文件最大为10kb,默认为0即不限制文件大小
|
||||
header:
|
||||
@@ -1077,7 +1077,7 @@ rule-providers:
|
||||
rule1:
|
||||
behavior: classical # domain ipcidr
|
||||
interval: 259200
|
||||
path: /path/to/save/file.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到任意位置,添加环境变量 SKIP_SAFE_PATH_CHECK=1
|
||||
path: /path/to/save/file.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到其他位置,请通过设置 SAFE_PATHS 环境变量指定额外的安全路径。该环境变量的语法同本操作系统的PATH环境变量解析规则(即Windows下以分号分割,其他系统下以冒号分割)
|
||||
type: http # http 的 path 可空置,默认储存路径为 homedir 的 rules 文件夹,文件名为 url 的 md5
|
||||
url: "url"
|
||||
proxy: DIRECT
|
||||
@@ -1276,6 +1276,16 @@ listeners:
|
||||
# - 0123456789abcdef
|
||||
# server-names:
|
||||
# - test.com
|
||||
# #下列两个 limit 为选填,可对未通过验证的回落连接限速,bytesPerSec 默认为 0 即不启用
|
||||
# #回落限速是一种特征,不建议启用,如果您是面板/一键脚本开发者,务必让这些参数随机化
|
||||
# limit-fallback-upload:
|
||||
# after-bytes: 0 # 传输指定字节后开始限速
|
||||
# bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||
# burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||
# limit-fallback-download:
|
||||
# after-bytes: 0 # 传输指定字节后开始限速
|
||||
# bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||
# burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||
|
||||
- name: tuic-in-1
|
||||
type: tuic
|
||||
@@ -1343,6 +1353,16 @@ listeners:
|
||||
- 0123456789abcdef
|
||||
server-names:
|
||||
- test.com
|
||||
#下列两个 limit 为选填,可对未通过验证的回落连接限速,bytesPerSec 默认为 0 即不启用
|
||||
#回落限速是一种特征,不建议启用,如果您是面板/一键脚本开发者,务必让这些参数随机化
|
||||
limit-fallback-upload:
|
||||
after-bytes: 0 # 传输指定字节后开始限速
|
||||
bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||
burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||
limit-fallback-download:
|
||||
after-bytes: 0 # 传输指定字节后开始限速
|
||||
bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||
burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||
### 注意,对于vless listener, 至少需要填写 “certificate和private-key” 或 “reality-config” 的其中一项 ###
|
||||
|
||||
- name: anytls-in-1
|
||||
@@ -1393,6 +1413,16 @@ listeners:
|
||||
# - 0123456789abcdef
|
||||
# server-names:
|
||||
# - test.com
|
||||
# #下列两个 limit 为选填,可对未通过验证的回落连接限速,bytesPerSec 默认为 0 即不启用
|
||||
# #回落限速是一种特征,不建议启用,如果您是面板/一键脚本开发者,务必让这些参数随机化
|
||||
# limit-fallback-upload:
|
||||
# after-bytes: 0 # 传输指定字节后开始限速
|
||||
# bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||
# burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||
# limit-fallback-download:
|
||||
# after-bytes: 0 # 传输指定字节后开始限速
|
||||
# bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||
# burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||
# ss-option: # like trojan-go's `shadowsocks` config
|
||||
# enabled: false
|
||||
# method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305
|
||||
|
||||
12
go.mod
12
go.mod
@@ -20,23 +20,23 @@ require (
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab
|
||||
github.com/metacubex/bart v0.20.5
|
||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399
|
||||
github.com/metacubex/chacha v0.1.2
|
||||
github.com/metacubex/chacha v0.1.5
|
||||
github.com/metacubex/fswatch v0.1.1
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
|
||||
github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639
|
||||
github.com/metacubex/randv2 v0.2.0
|
||||
github.com/metacubex/sing v0.5.3
|
||||
github.com/metacubex/sing v0.5.4-0.20250605054047-54dc6097da29
|
||||
github.com/metacubex/sing-mux v0.3.2
|
||||
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f
|
||||
github.com/metacubex/sing-shadowsocks v0.2.10
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.4
|
||||
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621023810-0e9ef9dd0c92
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621023950-93d605a2143d
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c
|
||||
github.com/metacubex/sing-tun v0.4.7-0.20250611091011-60774779fdd8
|
||||
github.com/metacubex/sing-vmess v0.2.2
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
|
||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
|
||||
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4
|
||||
github.com/metacubex/utls v1.7.3
|
||||
github.com/metacubex/utls v1.7.4-0.20250610022031-808d767c8c73
|
||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181
|
||||
github.com/miekg/dns v1.1.63 // lastest version compatible with golang1.20
|
||||
github.com/mroth/weightedrand/v2 v2.1.0
|
||||
|
||||
28
go.sum
28
go.sum
@@ -101,8 +101,8 @@ github.com/metacubex/bart v0.20.5 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM
|
||||
github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
|
||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
|
||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
|
||||
github.com/metacubex/chacha v0.1.2 h1:QulCq3eVm3TO6+4nVIWJtmSe7BT2GMrgVHuAoqRQnlc=
|
||||
github.com/metacubex/chacha v0.1.2/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
|
||||
github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M=
|
||||
github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
|
||||
github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU=
|
||||
github.com/metacubex/fswatch v0.1.1/go.mod h1:czrTT7Zlbz7vWft8RQu9Qqh+JoX+Nnb+UabuyN1YsgI=
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
|
||||
@@ -116,20 +116,24 @@ github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639/go.mod h1:Kc6
|
||||
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
|
||||
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
|
||||
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||
github.com/metacubex/sing v0.5.3 h1:QWdN16WFKMk06x4nzkc8SvZ7y2x+TLQrpkPoHs+WSVM=
|
||||
github.com/metacubex/sing v0.5.3/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||
github.com/metacubex/sing v0.5.4-0.20250605054047-54dc6097da29 h1:SD9q025FNTaepuFXFOKDhnGLVu6PQYChBvw2ZYPXeLo=
|
||||
github.com/metacubex/sing v0.5.4-0.20250605054047-54dc6097da29/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||
github.com/metacubex/sing-mux v0.3.2 h1:nJv52pyRivHcaZJKk2JgxpaVvj1GAXG81scSa9N7ncw=
|
||||
github.com/metacubex/sing-mux v0.3.2/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f h1:mP3vIm+9hRFI0C0Vl3pE0NESF/L85FDbuB0tGgUii6I=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.10 h1:Pr7LDbjMANIQHl07zWgl1vDuhpsfDQUpZ8cX6DPabfg=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.10/go.mod h1:MtRM0ZZjR0kaDOzy9zWSt6/4/UlrnsNBq+1FNAF4vBk=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.4 h1:Ec0x3hHR7xkld5Z09IGh16wtUUpBb2HgqZ9DExd8Q7s=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.4/go.mod h1:WP8+S0kqtnSbX1vlIpo5i8Irm/ijZITEPBcZ26B5unY=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621021503-4f85ef9bf4b3 h1:dtiRj7WaCAXp4UhCkmaIiFF6v886qXiuqeIDN4Z//9E=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621021503-4f85ef9bf4b3/go.mod h1:/squZ38pXrYjqtg8qn+joVvwbpGNYQNp8yxKsMVbCto=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621023810-0e9ef9dd0c92 h1:Y9ebcKya6ow7VHoESCN5+l4zZvg5eaL2IhI5LLCQxQA=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621023810-0e9ef9dd0c92/go.mod h1:/squZ38pXrYjqtg8qn+joVvwbpGNYQNp8yxKsMVbCto=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621021638-dcd503063651 h1:vwLj0DDjPYy4AHEZvfRVf8ih52o6wpBnJxXxqa+ztmE=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621021638-dcd503063651/go.mod h1:+ukTd0OPFglT3bnKAYTJWYPbuox6HYNXE235r5tHdUk=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621023950-93d605a2143d h1:Ey3A1tA8lVkRbK1FDmwuWj/57Nr8JMdpoVqe45mFzJg=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621023950-93d605a2143d/go.mod h1:+ukTd0OPFglT3bnKAYTJWYPbuox6HYNXE235r5tHdUk=
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c h1:Y6jk7AH5BEg9Dsvczrf/KokYsvxeKSZZlCLHg+hC4ro=
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250524142129-9d110c0af70c/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
|
||||
github.com/metacubex/sing-tun v0.4.7-0.20250611091011-60774779fdd8 h1:4zWKqxTx75TbfW2EmlQ3hxM6RTRg2PYOAVMCnU4I61I=
|
||||
github.com/metacubex/sing-tun v0.4.7-0.20250611091011-60774779fdd8/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64=
|
||||
github.com/metacubex/sing-vmess v0.2.2 h1:nG6GIKF1UOGmlzs+BIetdGHkFZ20YqFVIYp5Htqzp+4=
|
||||
github.com/metacubex/sing-vmess v0.2.2/go.mod h1:CVDNcdSLVYFgTHQlubr88d8CdqupAUDqLjROos+H9xk=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
|
||||
@@ -138,8 +142,8 @@ github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113a
|
||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE=
|
||||
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc=
|
||||
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||
github.com/metacubex/utls v1.7.3 h1:yDcMEWojFh+t8rU9X0HPcZDPAoFze/rIIyssqivzj8A=
|
||||
github.com/metacubex/utls v1.7.3/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU=
|
||||
github.com/metacubex/utls v1.7.4-0.20250610022031-808d767c8c73 h1:HWKsf92BqLYqugATFIJ3hYiEBZ7JF6AoqyvqF39afuI=
|
||||
github.com/metacubex/utls v1.7.4-0.20250610022031-808d767c8c73/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
|
||||
@@ -75,6 +75,7 @@ type tunSchema struct {
|
||||
AutoRedirect *bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"`
|
||||
AutoRedirectInputMark *uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"`
|
||||
AutoRedirectOutputMark *uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"`
|
||||
LoopbackAddress *[]netip.Addr `yaml:"loopback-address" json:"loopback-address,omitempty"`
|
||||
StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
RouteAddress *[]netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
|
||||
RouteAddressSet *[]string `yaml:"route-address-set" json:"route-address-set,omitempty"`
|
||||
@@ -174,6 +175,9 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun {
|
||||
if p.AutoRedirectOutputMark != nil {
|
||||
def.AutoRedirectOutputMark = *p.AutoRedirectOutputMark
|
||||
}
|
||||
if p.LoopbackAddress != nil {
|
||||
def.LoopbackAddress = *p.LoopbackAddress
|
||||
}
|
||||
if p.StrictRoute != nil {
|
||||
def.StrictRoute = *p.StrictRoute
|
||||
}
|
||||
|
||||
@@ -9,18 +9,6 @@ import (
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func StringSliceToNetipPrefixSlice(ss []string) ([]netip.Prefix, error) {
|
||||
lps := make([]netip.Prefix, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
prefix, err := netip.ParsePrefix(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lps = append(lps, prefix)
|
||||
}
|
||||
return lps, nil
|
||||
}
|
||||
|
||||
type Tun struct {
|
||||
Enable bool `yaml:"enable" json:"enable"`
|
||||
Device string `yaml:"device" json:"device"`
|
||||
@@ -39,6 +27,7 @@ type Tun struct {
|
||||
AutoRedirect bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"`
|
||||
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"`
|
||||
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"`
|
||||
LoopbackAddress []netip.Addr `yaml:"loopback-address" json:"loopback-address,omitempty"`
|
||||
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||
RouteAddress []netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
|
||||
RouteAddressSet []string `yaml:"route-address-set" json:"route-address-set,omitempty"`
|
||||
@@ -142,6 +131,9 @@ func (t *Tun) Equal(other Tun) bool {
|
||||
if t.AutoRedirectOutputMark != other.AutoRedirectOutputMark {
|
||||
return false
|
||||
}
|
||||
if !slices.Equal(t.RouteAddress, other.RouteAddress) {
|
||||
return false
|
||||
}
|
||||
if t.StrictRoute != other.StrictRoute {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -9,6 +9,15 @@ type RealityConfig struct {
|
||||
ServerNames []string `inbound:"server-names"`
|
||||
MaxTimeDifference int `inbound:"max-time-difference,omitempty"`
|
||||
Proxy string `inbound:"proxy,omitempty"`
|
||||
|
||||
LimitFallbackUpload RealityLimitFallback `inbound:"limit-fallback-upload,omitempty"`
|
||||
LimitFallbackDownload RealityLimitFallback `inbound:"limit-fallback-download,omitempty"`
|
||||
}
|
||||
|
||||
type RealityLimitFallback struct {
|
||||
AfterBytes uint64 `inbound:"after-bytes,omitempty"`
|
||||
BytesPerSec uint64 `inbound:"bytes-per-sec,omitempty"`
|
||||
BurstBytesPerSec uint64 `inbound:"burst-bytes-per-sec,omitempty"`
|
||||
}
|
||||
|
||||
func (c RealityConfig) Build() reality.Config {
|
||||
@@ -19,5 +28,16 @@ func (c RealityConfig) Build() reality.Config {
|
||||
ServerNames: c.ServerNames,
|
||||
MaxTimeDifference: c.MaxTimeDifference,
|
||||
Proxy: c.Proxy,
|
||||
|
||||
LimitFallbackUpload: reality.LimitFallback{
|
||||
AfterBytes: c.LimitFallbackUpload.AfterBytes,
|
||||
BytesPerSec: c.LimitFallbackUpload.BytesPerSec,
|
||||
BurstBytesPerSec: c.LimitFallbackUpload.BurstBytesPerSec,
|
||||
},
|
||||
LimitFallbackDownload: reality.LimitFallback{
|
||||
AfterBytes: c.LimitFallbackDownload.AfterBytes,
|
||||
BytesPerSec: c.LimitFallbackDownload.BytesPerSec,
|
||||
BurstBytesPerSec: c.LimitFallbackDownload.BurstBytesPerSec,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"encoding"
|
||||
"net/netip"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
@@ -12,50 +12,55 @@ import (
|
||||
|
||||
type TunOption struct {
|
||||
BaseOption
|
||||
Device string `inbound:"device,omitempty"`
|
||||
Stack string `inbound:"stack,omitempty"`
|
||||
DNSHijack []string `inbound:"dns-hijack,omitempty"`
|
||||
AutoRoute bool `inbound:"auto-route,omitempty"`
|
||||
AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"`
|
||||
Device string `inbound:"device,omitempty"`
|
||||
Stack C.TUNStack `inbound:"stack,omitempty"`
|
||||
DNSHijack []string `inbound:"dns-hijack,omitempty"`
|
||||
AutoRoute bool `inbound:"auto-route,omitempty"`
|
||||
AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"`
|
||||
|
||||
MTU uint32 `inbound:"mtu,omitempty"`
|
||||
GSO bool `inbound:"gso,omitempty"`
|
||||
GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"`
|
||||
Inet4Address []string `inbound:"inet4-address,omitempty"`
|
||||
Inet6Address []string `inbound:"inet6-address,omitempty"`
|
||||
IPRoute2TableIndex int `inbound:"iproute2-table-index,omitempty"`
|
||||
IPRoute2RuleIndex int `inbound:"iproute2-rule-index,omitempty"`
|
||||
AutoRedirect bool `inbound:"auto-redirect,omitempty"`
|
||||
AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark,omitempty"`
|
||||
AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark,omitempty"`
|
||||
StrictRoute bool `inbound:"strict-route,omitempty"`
|
||||
RouteAddress []string `inbound:"route-address,omitempty"`
|
||||
RouteAddressSet []string `inbound:"route-address-set,omitempty"`
|
||||
RouteExcludeAddress []string `inbound:"route-exclude-address,omitempty"`
|
||||
RouteExcludeAddressSet []string `inbound:"route-exclude-address-set,omitempty"`
|
||||
IncludeInterface []string `inbound:"include-interface,omitempty"`
|
||||
ExcludeInterface []string `inbound:"exclude-interface,omitempty"`
|
||||
IncludeUID []uint32 `inbound:"include-uid,omitempty"`
|
||||
IncludeUIDRange []string `inbound:"include-uid-range,omitempty"`
|
||||
ExcludeUID []uint32 `inbound:"exclude-uid,omitempty"`
|
||||
ExcludeUIDRange []string `inbound:"exclude-uid-range,omitempty"`
|
||||
ExcludeSrcPort []uint16 `inbound:"exclude-src-port,omitempty"`
|
||||
ExcludeSrcPortRange []string `inbound:"exclude-src-port-range,omitempty"`
|
||||
ExcludeDstPort []uint16 `inbound:"exclude-dst-port,omitempty"`
|
||||
ExcludeDstPortRange []string `inbound:"exclude-dst-port-range,omitempty"`
|
||||
IncludeAndroidUser []int `inbound:"include-android-user,omitempty"`
|
||||
IncludePackage []string `inbound:"include-package,omitempty"`
|
||||
ExcludePackage []string `inbound:"exclude-package,omitempty"`
|
||||
EndpointIndependentNat bool `inbound:"endpoint-independent-nat,omitempty"`
|
||||
UDPTimeout int64 `inbound:"udp-timeout,omitempty"`
|
||||
FileDescriptor int `inbound:"file-descriptor,omitempty"`
|
||||
MTU uint32 `inbound:"mtu,omitempty"`
|
||||
GSO bool `inbound:"gso,omitempty"`
|
||||
GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"`
|
||||
Inet4Address []netip.Prefix `inbound:"inet4-address,omitempty"`
|
||||
Inet6Address []netip.Prefix `inbound:"inet6-address,omitempty"`
|
||||
IPRoute2TableIndex int `inbound:"iproute2-table-index,omitempty"`
|
||||
IPRoute2RuleIndex int `inbound:"iproute2-rule-index,omitempty"`
|
||||
AutoRedirect bool `inbound:"auto-redirect,omitempty"`
|
||||
AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark,omitempty"`
|
||||
AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark,omitempty"`
|
||||
LoopbackAddress []netip.Addr `inbound:"loopback-address,omitempty"`
|
||||
StrictRoute bool `inbound:"strict-route,omitempty"`
|
||||
RouteAddress []netip.Prefix `inbound:"route-address,omitempty"`
|
||||
RouteAddressSet []string `inbound:"route-address-set,omitempty"`
|
||||
RouteExcludeAddress []netip.Prefix `inbound:"route-exclude-address,omitempty"`
|
||||
RouteExcludeAddressSet []string `inbound:"route-exclude-address-set,omitempty"`
|
||||
IncludeInterface []string `inbound:"include-interface,omitempty"`
|
||||
ExcludeInterface []string `inbound:"exclude-interface,omitempty"`
|
||||
IncludeUID []uint32 `inbound:"include-uid,omitempty"`
|
||||
IncludeUIDRange []string `inbound:"include-uid-range,omitempty"`
|
||||
ExcludeUID []uint32 `inbound:"exclude-uid,omitempty"`
|
||||
ExcludeUIDRange []string `inbound:"exclude-uid-range,omitempty"`
|
||||
ExcludeSrcPort []uint16 `inbound:"exclude-src-port,omitempty"`
|
||||
ExcludeSrcPortRange []string `inbound:"exclude-src-port-range,omitempty"`
|
||||
ExcludeDstPort []uint16 `inbound:"exclude-dst-port,omitempty"`
|
||||
ExcludeDstPortRange []string `inbound:"exclude-dst-port-range,omitempty"`
|
||||
IncludeAndroidUser []int `inbound:"include-android-user,omitempty"`
|
||||
IncludePackage []string `inbound:"include-package,omitempty"`
|
||||
ExcludePackage []string `inbound:"exclude-package,omitempty"`
|
||||
EndpointIndependentNat bool `inbound:"endpoint-independent-nat,omitempty"`
|
||||
UDPTimeout int64 `inbound:"udp-timeout,omitempty"`
|
||||
FileDescriptor int `inbound:"file-descriptor,omitempty"`
|
||||
|
||||
Inet4RouteAddress []string `inbound:"inet4-route-address,omitempty"`
|
||||
Inet6RouteAddress []string `inbound:"inet6-route-address,omitempty"`
|
||||
Inet4RouteExcludeAddress []string `inbound:"inet4-route-exclude-address,omitempty"`
|
||||
Inet6RouteExcludeAddress []string `inbound:"inet6-route-exclude-address,omitempty"`
|
||||
Inet4RouteAddress []netip.Prefix `inbound:"inet4-route-address,omitempty"`
|
||||
Inet6RouteAddress []netip.Prefix `inbound:"inet6-route-address,omitempty"`
|
||||
Inet4RouteExcludeAddress []netip.Prefix `inbound:"inet4-route-exclude-address,omitempty"`
|
||||
Inet6RouteExcludeAddress []netip.Prefix `inbound:"inet6-route-exclude-address,omitempty"`
|
||||
}
|
||||
|
||||
var _ encoding.TextUnmarshaler = (*netip.Addr)(nil) // ensure netip.Addr can decode direct by structure package
|
||||
var _ encoding.TextUnmarshaler = (*netip.Prefix)(nil) // ensure netip.Prefix can decode direct by structure package
|
||||
var _ encoding.TextUnmarshaler = (*C.TUNStack)(nil) // ensure C.TUNStack can decode direct by structure package
|
||||
|
||||
func (o TunOption) Equal(config C.InboundConfig) bool {
|
||||
return optionToString(o) == optionToString(config)
|
||||
}
|
||||
@@ -72,68 +77,31 @@ func NewTun(options *TunOption) (*Tun, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stack, exist := C.StackTypeMapping[strings.ToLower(options.Stack)]
|
||||
if !exist {
|
||||
return nil, errors.New("invalid tun stack")
|
||||
}
|
||||
|
||||
routeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
routeExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteExcludeAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inet4Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet4Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inet6Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet6Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inet4RouteAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet4RouteAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inet6RouteAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet6RouteAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inet4RouteExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet4RouteExcludeAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inet6RouteExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet6RouteExcludeAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Tun{
|
||||
Base: base,
|
||||
config: options,
|
||||
tun: LC.Tun{
|
||||
Enable: true,
|
||||
Device: options.Device,
|
||||
Stack: stack,
|
||||
Stack: options.Stack,
|
||||
DNSHijack: options.DNSHijack,
|
||||
AutoRoute: options.AutoRoute,
|
||||
AutoDetectInterface: options.AutoDetectInterface,
|
||||
MTU: options.MTU,
|
||||
GSO: options.GSO,
|
||||
GSOMaxSize: options.GSOMaxSize,
|
||||
Inet4Address: inet4Address,
|
||||
Inet6Address: inet6Address,
|
||||
Inet4Address: options.Inet4Address,
|
||||
Inet6Address: options.Inet6Address,
|
||||
IPRoute2TableIndex: options.IPRoute2TableIndex,
|
||||
IPRoute2RuleIndex: options.IPRoute2RuleIndex,
|
||||
AutoRedirect: options.AutoRedirect,
|
||||
AutoRedirectInputMark: options.AutoRedirectInputMark,
|
||||
AutoRedirectOutputMark: options.AutoRedirectOutputMark,
|
||||
LoopbackAddress: options.LoopbackAddress,
|
||||
StrictRoute: options.StrictRoute,
|
||||
RouteAddress: routeAddress,
|
||||
RouteAddress: options.RouteAddress,
|
||||
RouteAddressSet: options.RouteAddressSet,
|
||||
RouteExcludeAddress: routeExcludeAddress,
|
||||
RouteExcludeAddress: options.RouteExcludeAddress,
|
||||
RouteExcludeAddressSet: options.RouteExcludeAddressSet,
|
||||
IncludeInterface: options.IncludeInterface,
|
||||
ExcludeInterface: options.ExcludeInterface,
|
||||
@@ -152,10 +120,10 @@ func NewTun(options *TunOption) (*Tun, error) {
|
||||
UDPTimeout: options.UDPTimeout,
|
||||
FileDescriptor: options.FileDescriptor,
|
||||
|
||||
Inet4RouteAddress: inet4RouteAddress,
|
||||
Inet6RouteAddress: inet6RouteAddress,
|
||||
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
|
||||
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
|
||||
Inet4RouteAddress: options.Inet4RouteAddress,
|
||||
Inet6RouteAddress: options.Inet6RouteAddress,
|
||||
Inet4RouteExcludeAddress: options.Inet4RouteExcludeAddress,
|
||||
Inet6RouteExcludeAddress: options.Inet6RouteExcludeAddress,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) {
|
||||
listener, err = IN.NewTunnel(tunnelOption)
|
||||
case "tun":
|
||||
tunOption := &IN.TunOption{
|
||||
Stack: C.TunGvisor.String(),
|
||||
Stack: C.TunGvisor,
|
||||
DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query
|
||||
}
|
||||
err = decoder.Decode(mapping, tunOption)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
)
|
||||
|
||||
type Conn = utls.Conn
|
||||
type LimitFallback = utls.RealityLimitFallback
|
||||
|
||||
type Config struct {
|
||||
Dest string
|
||||
@@ -28,6 +29,9 @@ type Config struct {
|
||||
ServerNames []string
|
||||
MaxTimeDifference int
|
||||
Proxy string
|
||||
|
||||
LimitFallbackUpload LimitFallback
|
||||
LimitFallbackDownload LimitFallback
|
||||
}
|
||||
|
||||
func (c Config) Build(tunnel C.Tunnel) (*Builder, error) {
|
||||
@@ -73,6 +77,9 @@ func (c Config) Build(tunnel C.Tunnel) (*Builder, error) {
|
||||
return inner.HandleTcp(tunnel, address, c.Proxy)
|
||||
}
|
||||
|
||||
realityConfig.LimitFallbackUpload = c.LimitFallbackUpload
|
||||
realityConfig.LimitFallbackDownload = c.LimitFallbackDownload
|
||||
|
||||
return &Builder{realityConfig}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -347,6 +347,8 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
||||
IPRoute2RuleIndex: ruleIndex,
|
||||
AutoRedirectInputMark: inputMark,
|
||||
AutoRedirectOutputMark: outputMark,
|
||||
Inet4LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is4),
|
||||
Inet6LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is6),
|
||||
StrictRoute: options.StrictRoute,
|
||||
Inet4RouteAddress: inet4RouteAddress,
|
||||
Inet6RouteAddress: inet6RouteAddress,
|
||||
|
||||
35
log/level.go
35
log/level.go
@@ -1,7 +1,6 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
@@ -25,30 +24,6 @@ const (
|
||||
|
||||
type LogLevel int
|
||||
|
||||
// UnmarshalYAML unserialize LogLevel with yaml
|
||||
func (l *LogLevel) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var tp string
|
||||
unmarshal(&tp)
|
||||
level, exist := LogLevelMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid log-level")
|
||||
}
|
||||
*l = level
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON unserialize LogLevel with json
|
||||
func (l *LogLevel) UnmarshalJSON(data []byte) error {
|
||||
var tp string
|
||||
json.Unmarshal(data, &tp)
|
||||
level, exist := LogLevelMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid log-level")
|
||||
}
|
||||
*l = level
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalText unserialize LogLevel
|
||||
func (l *LogLevel) UnmarshalText(data []byte) error {
|
||||
level, exist := LogLevelMapping[strings.ToLower(string(data))]
|
||||
@@ -59,16 +34,6 @@ func (l *LogLevel) UnmarshalText(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML serialize LogLevel with yaml
|
||||
func (l LogLevel) MarshalYAML() (any, error) {
|
||||
return l.String(), nil
|
||||
}
|
||||
|
||||
// MarshalJSON serialize LogLevel with json
|
||||
func (l LogLevel) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(l.String())
|
||||
}
|
||||
|
||||
// MarshalText serialize LogLevel
|
||||
func (l LogLevel) MarshalText() ([]byte, error) {
|
||||
return []byte(l.String()), nil
|
||||
|
||||
@@ -21,14 +21,6 @@ var (
|
||||
type Base struct {
|
||||
}
|
||||
|
||||
func (b *Base) ShouldFindProcess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Base) ShouldResolveIP() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Base) ProviderNames() []string { return nil }
|
||||
|
||||
func ParseParams(params []string) (isSrc bool, noResolve bool) {
|
||||
|
||||
@@ -17,7 +17,7 @@ func (d *Domain) RuleType() C.RuleType {
|
||||
return C.Domain
|
||||
}
|
||||
|
||||
func (d *Domain) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (d *Domain) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
return metadata.RuleHost() == d.domain, d.adapter
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ func (dk *DomainKeyword) RuleType() C.RuleType {
|
||||
return C.DomainKeyword
|
||||
}
|
||||
|
||||
func (dk *DomainKeyword) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (dk *DomainKeyword) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
domain := metadata.RuleHost()
|
||||
return strings.Contains(domain, dk.keyword), dk.adapter
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func (dr *DomainRegex) RuleType() C.RuleType {
|
||||
return C.DomainRegex
|
||||
}
|
||||
|
||||
func (dr *DomainRegex) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (dr *DomainRegex) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
domain := metadata.RuleHost()
|
||||
match, _ := dr.regex.MatchString(domain)
|
||||
return match, dr.adapter
|
||||
|
||||
@@ -17,7 +17,7 @@ func (ds *DomainSuffix) RuleType() C.RuleType {
|
||||
return C.DomainSuffix
|
||||
}
|
||||
|
||||
func (ds *DomainSuffix) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (ds *DomainSuffix) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
domain := metadata.RuleHost()
|
||||
return strings.HasSuffix(domain, "."+ds.suffix) || domain == ds.suffix, ds.adapter
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func (d *DSCP) RuleType() C.RuleType {
|
||||
return C.DSCP
|
||||
}
|
||||
|
||||
func (d *DSCP) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (d *DSCP) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
return d.ranges.Check(metadata.DSCP), d.adapter
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ func (f *Match) RuleType() C.RuleType {
|
||||
return C.MATCH
|
||||
}
|
||||
|
||||
func (f *Match) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (f *Match) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
return true, f.adapter
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@ func (g *GEOIP) RuleType() C.RuleType {
|
||||
return C.GEOIP
|
||||
}
|
||||
|
||||
func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (g *GEOIP) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
if !g.noResolveIP && !g.isSourceIP && helper.ResolveIP != nil {
|
||||
helper.ResolveIP()
|
||||
}
|
||||
|
||||
ip := metadata.DstIP
|
||||
if g.isSourceIP {
|
||||
ip = metadata.SrcIP
|
||||
@@ -161,10 +165,6 @@ func (g *GEOIP) Payload() string {
|
||||
return g.country
|
||||
}
|
||||
|
||||
func (g *GEOIP) ShouldResolveIP() bool {
|
||||
return !g.noResolveIP
|
||||
}
|
||||
|
||||
func (g *GEOIP) GetCountry() string {
|
||||
return g.country
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func (gs *GEOSITE) RuleType() C.RuleType {
|
||||
return C.GEOSITE
|
||||
}
|
||||
|
||||
func (gs *GEOSITE) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (gs *GEOSITE) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
return gs.MatchDomain(metadata.RuleHost()), gs.adapter
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
||||
type InName struct {
|
||||
@@ -13,7 +14,7 @@ type InName struct {
|
||||
payload string
|
||||
}
|
||||
|
||||
func (u *InName) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (u *InName) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
for _, name := range u.names {
|
||||
if metadata.InName == name {
|
||||
return true, u.adapter
|
||||
@@ -36,8 +37,12 @@ func (u *InName) Payload() string {
|
||||
|
||||
func NewInName(iNames, adapter string) (*InName, error) {
|
||||
names := strings.Split(iNames, "/")
|
||||
if len(names) == 0 {
|
||||
return nil, fmt.Errorf("in name couldn't be empty")
|
||||
for i, name := range names {
|
||||
name = strings.TrimSpace(name)
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("in name couldn't be empty")
|
||||
}
|
||||
names[i] = name
|
||||
}
|
||||
|
||||
return &InName{
|
||||
|
||||
@@ -2,8 +2,9 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
||||
type InType struct {
|
||||
@@ -13,7 +14,7 @@ type InType struct {
|
||||
payload string
|
||||
}
|
||||
|
||||
func (u *InType) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (u *InType) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
for _, tp := range u.types {
|
||||
if metadata.Type == tp {
|
||||
return true, u.adapter
|
||||
@@ -36,8 +37,12 @@ func (u *InType) Payload() string {
|
||||
|
||||
func NewInType(iTypes, adapter string) (*InType, error) {
|
||||
types := strings.Split(iTypes, "/")
|
||||
if len(types) == 0 {
|
||||
return nil, fmt.Errorf("in type couldn't be empty")
|
||||
for i, tp := range types {
|
||||
tp = strings.TrimSpace(tp)
|
||||
if len(tp) == 0 {
|
||||
return nil, fmt.Errorf("in type couldn't be empty")
|
||||
}
|
||||
types[i] = tp
|
||||
}
|
||||
|
||||
tps, err := parseInTypes(types)
|
||||
|
||||
@@ -2,8 +2,9 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
||||
type InUser struct {
|
||||
@@ -13,7 +14,7 @@ type InUser struct {
|
||||
payload string
|
||||
}
|
||||
|
||||
func (u *InUser) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (u *InUser) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
for _, user := range u.users {
|
||||
if metadata.InUser == user {
|
||||
return true, u.adapter
|
||||
@@ -36,8 +37,12 @@ func (u *InUser) Payload() string {
|
||||
|
||||
func NewInUser(iUsers, adapter string) (*InUser, error) {
|
||||
users := strings.Split(iUsers, "/")
|
||||
if len(users) == 0 {
|
||||
return nil, fmt.Errorf("in user couldn't be empty")
|
||||
for i, user := range users {
|
||||
user = strings.TrimSpace(user)
|
||||
if len(user) == 0 {
|
||||
return nil, fmt.Errorf("in user couldn't be empty")
|
||||
}
|
||||
users[i] = user
|
||||
}
|
||||
|
||||
return &InUser{
|
||||
|
||||
@@ -15,7 +15,11 @@ type ASN struct {
|
||||
isSourceIP bool
|
||||
}
|
||||
|
||||
func (a *ASN) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (a *ASN) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
if !a.noResolveIP && !a.isSourceIP && helper.ResolveIP != nil {
|
||||
helper.ResolveIP()
|
||||
}
|
||||
|
||||
ip := metadata.DstIP
|
||||
if a.isSourceIP {
|
||||
ip = metadata.SrcIP
|
||||
@@ -49,10 +53,6 @@ func (a *ASN) Payload() string {
|
||||
return a.asn
|
||||
}
|
||||
|
||||
func (a *ASN) ShouldResolveIP() bool {
|
||||
return !a.noResolveIP
|
||||
}
|
||||
|
||||
func (a *ASN) GetASN() string {
|
||||
return a.asn
|
||||
}
|
||||
|
||||
@@ -35,7 +35,11 @@ func (i *IPCIDR) RuleType() C.RuleType {
|
||||
return C.IPCIDR
|
||||
}
|
||||
|
||||
func (i *IPCIDR) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (i *IPCIDR) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
if !i.noResolveIP && !i.isSourceIP && helper.ResolveIP != nil {
|
||||
helper.ResolveIP()
|
||||
}
|
||||
|
||||
ip := metadata.DstIP
|
||||
if i.isSourceIP {
|
||||
ip = metadata.SrcIP
|
||||
@@ -51,10 +55,6 @@ func (i *IPCIDR) Payload() string {
|
||||
return i.ipnet.String()
|
||||
}
|
||||
|
||||
func (i *IPCIDR) ShouldResolveIP() bool {
|
||||
return !i.noResolveIP
|
||||
}
|
||||
|
||||
func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) {
|
||||
ipnet, err := netip.ParsePrefix(s)
|
||||
if err != nil {
|
||||
|
||||
@@ -22,7 +22,11 @@ func (is *IPSuffix) RuleType() C.RuleType {
|
||||
return C.IPSuffix
|
||||
}
|
||||
|
||||
func (is *IPSuffix) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (is *IPSuffix) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
if !is.noResolveIP && !is.isSourceIP && helper.ResolveIP != nil {
|
||||
helper.ResolveIP()
|
||||
}
|
||||
|
||||
ip := metadata.DstIP
|
||||
if is.isSourceIP {
|
||||
ip = metadata.SrcIP
|
||||
@@ -57,10 +61,6 @@ func (is *IPSuffix) Payload() string {
|
||||
return is.payload
|
||||
}
|
||||
|
||||
func (is *IPSuffix) ShouldResolveIP() bool {
|
||||
return !is.noResolveIP
|
||||
}
|
||||
|
||||
func NewIPSuffix(payload, adapter string, isSrc, noResolveIP bool) (*IPSuffix, error) {
|
||||
ipnet, err := netip.ParsePrefix(payload)
|
||||
if err != nil {
|
||||
|
||||
@@ -34,7 +34,7 @@ func (n *NetworkType) RuleType() C.RuleType {
|
||||
return C.Network
|
||||
}
|
||||
|
||||
func (n *NetworkType) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (n *NetworkType) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
return n.network == metadata.NetWork, n.adapter
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ func (p *Port) RuleType() C.RuleType {
|
||||
return p.ruleType
|
||||
}
|
||||
|
||||
func (p *Port) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (p *Port) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
targetPort := metadata.DstPort
|
||||
switch p.ruleType {
|
||||
case C.InPort:
|
||||
|
||||
@@ -30,7 +30,10 @@ func (ps *Process) RuleType() C.RuleType {
|
||||
return C.ProcessPath
|
||||
}
|
||||
|
||||
func (ps *Process) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (ps *Process) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
if helper.FindProcess != nil {
|
||||
helper.FindProcess()
|
||||
}
|
||||
if ps.nameOnly {
|
||||
if ps.regexp != nil {
|
||||
match, _ := ps.regexp.MatchString(metadata.Process)
|
||||
@@ -54,10 +57,6 @@ func (ps *Process) Payload() string {
|
||||
return ps.process
|
||||
}
|
||||
|
||||
func (ps *Process) ShouldFindProcess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func NewProcess(process string, adapter string, nameOnly bool, regex bool) (*Process, error) {
|
||||
var r *regexp2.Regexp
|
||||
var err error
|
||||
|
||||
@@ -41,7 +41,10 @@ func (u *Uid) RuleType() C.RuleType {
|
||||
return C.Uid
|
||||
}
|
||||
|
||||
func (u *Uid) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (u *Uid) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
if helper.FindProcess != nil {
|
||||
helper.FindProcess()
|
||||
}
|
||||
if metadata.Uid != 0 {
|
||||
if u.uids.Check(metadata.Uid) {
|
||||
return true, u.adapter
|
||||
@@ -58,7 +61,3 @@ func (u *Uid) Adapter() string {
|
||||
func (u *Uid) Payload() string {
|
||||
return u.oUid
|
||||
}
|
||||
|
||||
func (u *Uid) ShouldFindProcess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -195,11 +195,11 @@ func (logic *Logic) RuleType() C.RuleType {
|
||||
return logic.ruleType
|
||||
}
|
||||
|
||||
func matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Rule) (bool, string) {
|
||||
func matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Rule, helper C.RuleMatchHelper) (bool, string) {
|
||||
for _, rule := range subRules[name] {
|
||||
if m, a := rule.Match(metadata); m {
|
||||
if m, a := rule.Match(metadata, helper); m {
|
||||
if rule.RuleType() == C.SubRules {
|
||||
return matchSubRules(metadata, rule.Adapter(), subRules)
|
||||
return matchSubRules(metadata, rule.Adapter(), subRules, helper)
|
||||
} else {
|
||||
return m, a
|
||||
}
|
||||
@@ -208,28 +208,28 @@ func matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Ru
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func (logic *Logic) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (logic *Logic) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
switch logic.ruleType {
|
||||
case C.SubRules:
|
||||
if m, _ := logic.rules[0].Match(metadata); m {
|
||||
return matchSubRules(metadata, logic.adapter, logic.subRules)
|
||||
if m, _ := logic.rules[0].Match(metadata, helper); m {
|
||||
return matchSubRules(metadata, logic.adapter, logic.subRules, helper)
|
||||
}
|
||||
return false, ""
|
||||
case C.NOT:
|
||||
if m, _ := logic.rules[0].Match(metadata); !m {
|
||||
if m, _ := logic.rules[0].Match(metadata, helper); !m {
|
||||
return true, logic.adapter
|
||||
}
|
||||
return false, ""
|
||||
case C.OR:
|
||||
for _, rule := range logic.rules {
|
||||
if m, _ := rule.Match(metadata); m {
|
||||
if m, _ := rule.Match(metadata, helper); m {
|
||||
return true, logic.adapter
|
||||
}
|
||||
}
|
||||
return false, ""
|
||||
case C.AND:
|
||||
for _, rule := range logic.rules {
|
||||
if m, _ := rule.Match(metadata); !m {
|
||||
if m, _ := rule.Match(metadata, helper); !m {
|
||||
return false, logic.adapter
|
||||
}
|
||||
}
|
||||
@@ -266,38 +266,6 @@ func (logic *Logic) Payload() string {
|
||||
return logic.payload
|
||||
}
|
||||
|
||||
func (logic *Logic) ShouldResolveIP() bool {
|
||||
if logic.ruleType == C.SubRules {
|
||||
for _, rule := range logic.subRules[logic.adapter] {
|
||||
if rule.ShouldResolveIP() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, rule := range logic.rules {
|
||||
if rule.ShouldResolveIP() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (logic *Logic) ShouldFindProcess() bool {
|
||||
if logic.ruleType == C.SubRules {
|
||||
for _, rule := range logic.subRules[logic.adapter] {
|
||||
if rule.ShouldFindProcess() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, rule := range logic.rules {
|
||||
if rule.ShouldFindProcess() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (logic *Logic) ProviderNames() (names []string) {
|
||||
for _, rule := range logic.rules {
|
||||
names = append(names, rule.ProviderNames()...)
|
||||
|
||||
@@ -16,12 +16,11 @@ func TestAND(t *testing.T) {
|
||||
and, err := NewAND("((DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule)
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "DIRECT", and.Adapter())
|
||||
assert.Equal(t, false, and.ShouldResolveIP())
|
||||
m, _ := and.Match(&C.Metadata{
|
||||
Host: "baidu.com",
|
||||
NetWork: C.TCP,
|
||||
DstPort: 20000,
|
||||
})
|
||||
}, C.RuleMatchHelper{})
|
||||
assert.Equal(t, true, m)
|
||||
|
||||
and, err = NewAND("(DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule)
|
||||
@@ -36,7 +35,7 @@ func TestNOT(t *testing.T) {
|
||||
assert.Equal(t, nil, err)
|
||||
m, _ := not.Match(&C.Metadata{
|
||||
DstPort: 6100,
|
||||
})
|
||||
}, C.RuleMatchHelper{})
|
||||
assert.Equal(t, false, m)
|
||||
|
||||
_, err = NewNOT("((DST-PORT,5600-6666),(DOMAIN,baidu.com))", "DIRECT", ParseRule)
|
||||
@@ -51,7 +50,6 @@ func TestOR(t *testing.T) {
|
||||
assert.Equal(t, nil, err)
|
||||
m, _ := or.Match(&C.Metadata{
|
||||
NetWork: C.TCP,
|
||||
})
|
||||
}, C.RuleMatchHelper{})
|
||||
assert.Equal(t, true, m)
|
||||
assert.Equal(t, false, or.ShouldResolveIP())
|
||||
}
|
||||
|
||||
@@ -10,20 +10,18 @@ import (
|
||||
)
|
||||
|
||||
type classicalStrategy struct {
|
||||
rules []C.Rule
|
||||
count int
|
||||
shouldResolveIP bool
|
||||
shouldFindProcess bool
|
||||
parse func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error)
|
||||
rules []C.Rule
|
||||
count int
|
||||
parse func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error)
|
||||
}
|
||||
|
||||
func (c *classicalStrategy) Behavior() P.RuleBehavior {
|
||||
return P.Classical
|
||||
}
|
||||
|
||||
func (c *classicalStrategy) Match(metadata *C.Metadata) bool {
|
||||
func (c *classicalStrategy) Match(metadata *C.Metadata, helper C.RuleMatchHelper) bool {
|
||||
for _, rule := range c.rules {
|
||||
if m, _ := rule.Match(metadata); m {
|
||||
if m, _ := rule.Match(metadata, helper); m {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -35,39 +33,17 @@ func (c *classicalStrategy) Count() int {
|
||||
return c.count
|
||||
}
|
||||
|
||||
func (c *classicalStrategy) ShouldResolveIP() bool {
|
||||
return c.shouldResolveIP
|
||||
}
|
||||
|
||||
func (c *classicalStrategy) ShouldFindProcess() bool {
|
||||
return c.shouldFindProcess
|
||||
}
|
||||
|
||||
func (c *classicalStrategy) Reset() {
|
||||
c.rules = nil
|
||||
c.count = 0
|
||||
c.shouldFindProcess = false
|
||||
c.shouldResolveIP = false
|
||||
}
|
||||
|
||||
func (c *classicalStrategy) Insert(rule string) {
|
||||
ruleType, rule, params := ruleParse(rule)
|
||||
|
||||
if ruleType == "PROCESS-NAME" {
|
||||
c.shouldFindProcess = true
|
||||
}
|
||||
|
||||
r, err := c.parse(ruleType, rule, "", params)
|
||||
if err != nil {
|
||||
log.Warnln("parse classical rule error: %s", err.Error())
|
||||
} else {
|
||||
if r.ShouldResolveIP() {
|
||||
c.shouldResolveIP = true
|
||||
}
|
||||
if r.ShouldFindProcess() {
|
||||
c.shouldFindProcess = true
|
||||
}
|
||||
|
||||
c.rules = append(c.rules, r)
|
||||
c.count++
|
||||
}
|
||||
|
||||
@@ -23,11 +23,7 @@ func (d *domainStrategy) Behavior() P.RuleBehavior {
|
||||
return P.Domain
|
||||
}
|
||||
|
||||
func (d *domainStrategy) ShouldFindProcess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *domainStrategy) Match(metadata *C.Metadata) bool {
|
||||
func (d *domainStrategy) Match(metadata *C.Metadata, helper C.RuleMatchHelper) bool {
|
||||
return d.domainSet != nil && d.domainSet.Has(metadata.RuleHost())
|
||||
}
|
||||
|
||||
@@ -35,10 +31,6 @@ func (d *domainStrategy) Count() int {
|
||||
return d.count
|
||||
}
|
||||
|
||||
func (d *domainStrategy) ShouldResolveIP() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *domainStrategy) Reset() {
|
||||
d.domainTrie = trie.New[struct{}]()
|
||||
d.domainSet = nil
|
||||
|
||||
@@ -14,21 +14,19 @@ import (
|
||||
)
|
||||
|
||||
type ipcidrStrategy struct {
|
||||
count int
|
||||
shouldResolveIP bool
|
||||
cidrSet *cidr.IpCidrSet
|
||||
//trie *trie.IpCidrTrie
|
||||
count int
|
||||
cidrSet *cidr.IpCidrSet
|
||||
//trie *trie.IpCidrTrie
|
||||
}
|
||||
|
||||
func (i *ipcidrStrategy) Behavior() P.RuleBehavior {
|
||||
return P.IPCIDR
|
||||
}
|
||||
|
||||
func (i *ipcidrStrategy) ShouldFindProcess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *ipcidrStrategy) Match(metadata *C.Metadata) bool {
|
||||
func (i *ipcidrStrategy) Match(metadata *C.Metadata, helper C.RuleMatchHelper) bool {
|
||||
if helper.ResolveIP != nil {
|
||||
helper.ResolveIP()
|
||||
}
|
||||
// return i.trie != nil && i.trie.IsContain(metadata.DstIP.AsSlice())
|
||||
return i.cidrSet != nil && i.cidrSet.IsContain(metadata.DstIP)
|
||||
}
|
||||
@@ -37,15 +35,10 @@ func (i *ipcidrStrategy) Count() int {
|
||||
return i.count
|
||||
}
|
||||
|
||||
func (i *ipcidrStrategy) ShouldResolveIP() bool {
|
||||
return i.shouldResolveIP
|
||||
}
|
||||
|
||||
func (i *ipcidrStrategy) Reset() {
|
||||
// i.trie = trie.NewIpCidrTrie()
|
||||
i.cidrSet = cidr.NewIpCidrSet()
|
||||
i.count = 0
|
||||
i.shouldResolveIP = false
|
||||
}
|
||||
|
||||
func (i *ipcidrStrategy) Insert(rule string) {
|
||||
@@ -54,7 +47,6 @@ func (i *ipcidrStrategy) Insert(rule string) {
|
||||
if err != nil {
|
||||
log.Warnln("invalid Ipcidr:[%s]", rule)
|
||||
} else {
|
||||
i.shouldResolveIP = true
|
||||
i.count++
|
||||
}
|
||||
}
|
||||
@@ -70,9 +62,6 @@ func (i *ipcidrStrategy) FromMrs(r io.Reader, count int) error {
|
||||
}
|
||||
i.count = count
|
||||
i.cidrSet = cidrSet
|
||||
if i.count > 0 {
|
||||
i.shouldResolveIP = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -46,10 +46,8 @@ type providerForApi struct {
|
||||
|
||||
type ruleStrategy interface {
|
||||
Behavior() P.RuleBehavior
|
||||
Match(metadata *C.Metadata) bool
|
||||
Match(metadata *C.Metadata, helper C.RuleMatchHelper) bool
|
||||
Count() int
|
||||
ShouldResolveIP() bool
|
||||
ShouldFindProcess() bool
|
||||
Reset()
|
||||
Insert(rule string)
|
||||
FinishInsert()
|
||||
@@ -79,16 +77,8 @@ func (bp *baseProvider) Count() int {
|
||||
return bp.strategy.Count()
|
||||
}
|
||||
|
||||
func (bp *baseProvider) Match(metadata *C.Metadata) bool {
|
||||
return bp.strategy != nil && bp.strategy.Match(metadata)
|
||||
}
|
||||
|
||||
func (bp *baseProvider) ShouldResolveIP() bool {
|
||||
return bp.strategy.ShouldResolveIP()
|
||||
}
|
||||
|
||||
func (bp *baseProvider) ShouldFindProcess() bool {
|
||||
return bp.strategy.ShouldFindProcess()
|
||||
func (bp *baseProvider) Match(metadata *C.Metadata, helper C.RuleMatchHelper) bool {
|
||||
return bp.strategy != nil && bp.strategy.Match(metadata, helper)
|
||||
}
|
||||
|
||||
func (bp *baseProvider) Strategy() any {
|
||||
|
||||
@@ -10,47 +10,40 @@ import (
|
||||
|
||||
type RuleSet struct {
|
||||
*common.Base
|
||||
ruleProviderName string
|
||||
adapter string
|
||||
isSrc bool
|
||||
noResolveIP bool
|
||||
shouldFindProcess bool
|
||||
}
|
||||
|
||||
func (rs *RuleSet) ShouldFindProcess() bool {
|
||||
if rs.shouldFindProcess {
|
||||
return true
|
||||
}
|
||||
if provider, ok := rs.getProvider(); ok {
|
||||
return provider.ShouldFindProcess()
|
||||
}
|
||||
return false
|
||||
ruleProviderName string
|
||||
adapter string
|
||||
isSrc bool
|
||||
noResolveIP bool
|
||||
}
|
||||
|
||||
func (rs *RuleSet) RuleType() C.RuleType {
|
||||
return C.RuleSet
|
||||
}
|
||||
|
||||
func (rs *RuleSet) Match(metadata *C.Metadata) (bool, string) {
|
||||
func (rs *RuleSet) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||
if provider, ok := rs.getProvider(); ok {
|
||||
if rs.isSrc {
|
||||
metadata.SwapSrcDst()
|
||||
defer metadata.SwapSrcDst()
|
||||
|
||||
helper.ResolveIP = nil // src mode should not resolve ip
|
||||
} else if rs.noResolveIP {
|
||||
helper.ResolveIP = nil
|
||||
}
|
||||
return provider.Match(metadata), rs.adapter
|
||||
return provider.Match(metadata, helper), rs.adapter
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// MatchDomain implements C.DomainMatcher
|
||||
func (rs *RuleSet) MatchDomain(domain string) bool {
|
||||
ok, _ := rs.Match(&C.Metadata{Host: domain})
|
||||
ok, _ := rs.Match(&C.Metadata{Host: domain}, C.RuleMatchHelper{})
|
||||
return ok
|
||||
}
|
||||
|
||||
// MatchIp implements C.IpMatcher
|
||||
func (rs *RuleSet) MatchIp(ip netip.Addr) bool {
|
||||
ok, _ := rs.Match(&C.Metadata{DstIP: ip})
|
||||
ok, _ := rs.Match(&C.Metadata{DstIP: ip}, C.RuleMatchHelper{})
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -62,16 +55,6 @@ func (rs *RuleSet) Payload() string {
|
||||
return rs.ruleProviderName
|
||||
}
|
||||
|
||||
func (rs *RuleSet) ShouldResolveIP() bool {
|
||||
if rs.noResolveIP {
|
||||
return false
|
||||
}
|
||||
if provider, ok := rs.getProvider(); ok {
|
||||
return provider.ShouldResolveIP()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rs *RuleSet) ProviderNames() []string {
|
||||
return []string{rs.ruleProviderName}
|
||||
}
|
||||
|
||||
55
transport/shadowsocks/shadowstream/chacha20.go
Normal file
55
transport/shadowsocks/shadowstream/chacha20.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package shadowstream
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
|
||||
"github.com/metacubex/chacha"
|
||||
)
|
||||
|
||||
func newChaCha20(nonce, key []byte) cipher.Stream {
|
||||
c, err := chacha.NewChaCha20IgnoreCounterOverflow(nonce, key)
|
||||
if err != nil {
|
||||
panic(err) // should never happen
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
type chacha20key []byte
|
||||
|
||||
func (k chacha20key) IVSize() int { return chacha.NonceSize }
|
||||
func (k chacha20key) Encrypter(iv []byte) cipher.Stream { return newChaCha20(iv, k) }
|
||||
func (k chacha20key) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) }
|
||||
|
||||
func ChaCha20(key []byte) (Cipher, error) {
|
||||
if len(key) != chacha.KeySize {
|
||||
return nil, KeySizeError(chacha.KeySize)
|
||||
}
|
||||
return chacha20key(key), nil
|
||||
}
|
||||
|
||||
// IETF-variant of chacha20
|
||||
type chacha20ietfkey []byte
|
||||
|
||||
func (k chacha20ietfkey) IVSize() int { return chacha.INonceSize }
|
||||
func (k chacha20ietfkey) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) }
|
||||
func (k chacha20ietfkey) Encrypter(iv []byte) cipher.Stream { return newChaCha20(iv, k) }
|
||||
|
||||
func Chacha20IETF(key []byte) (Cipher, error) {
|
||||
if len(key) != chacha.KeySize {
|
||||
return nil, KeySizeError(chacha.KeySize)
|
||||
}
|
||||
return chacha20ietfkey(key), nil
|
||||
}
|
||||
|
||||
type xchacha20key []byte
|
||||
|
||||
func (k xchacha20key) IVSize() int { return chacha.XNonceSize }
|
||||
func (k xchacha20key) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) }
|
||||
func (k xchacha20key) Encrypter(iv []byte) cipher.Stream { return newChaCha20(iv, k) }
|
||||
|
||||
func Xchacha20(key []byte) (Cipher, error) {
|
||||
if len(key) != chacha.KeySize {
|
||||
return nil, KeySizeError(chacha.KeySize)
|
||||
}
|
||||
return xchacha20key(key), nil
|
||||
}
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"crypto/md5"
|
||||
"crypto/rc4"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/chacha20"
|
||||
)
|
||||
|
||||
// Cipher generates a pair of stream ciphers for encryption and decryption.
|
||||
@@ -53,45 +51,6 @@ func AESCFB(key []byte) (Cipher, error) {
|
||||
return &cfbStream{blk}, nil
|
||||
}
|
||||
|
||||
// IETF-variant of chacha20
|
||||
type chacha20ietfkey []byte
|
||||
|
||||
func (k chacha20ietfkey) IVSize() int { return chacha20.NonceSize }
|
||||
func (k chacha20ietfkey) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) }
|
||||
func (k chacha20ietfkey) Encrypter(iv []byte) cipher.Stream {
|
||||
ciph, err := chacha20.NewUnauthenticatedCipher(k, iv)
|
||||
if err != nil {
|
||||
panic(err) // should never happen
|
||||
}
|
||||
return ciph
|
||||
}
|
||||
|
||||
func Chacha20IETF(key []byte) (Cipher, error) {
|
||||
if len(key) != chacha20.KeySize {
|
||||
return nil, KeySizeError(chacha20.KeySize)
|
||||
}
|
||||
return chacha20ietfkey(key), nil
|
||||
}
|
||||
|
||||
type xchacha20key []byte
|
||||
|
||||
func (k xchacha20key) IVSize() int { return chacha20.NonceSizeX }
|
||||
func (k xchacha20key) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) }
|
||||
func (k xchacha20key) Encrypter(iv []byte) cipher.Stream {
|
||||
ciph, err := chacha20.NewUnauthenticatedCipher(k, iv)
|
||||
if err != nil {
|
||||
panic(err) // should never happen
|
||||
}
|
||||
return ciph
|
||||
}
|
||||
|
||||
func Xchacha20(key []byte) (Cipher, error) {
|
||||
if len(key) != chacha20.KeySize {
|
||||
return nil, KeySizeError(chacha20.KeySize)
|
||||
}
|
||||
return xchacha20key(key), nil
|
||||
}
|
||||
|
||||
type rc4Md5Key []byte
|
||||
|
||||
func (k rc4Md5Key) IVSize() int {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package shadowstream
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
|
||||
"github.com/metacubex/chacha"
|
||||
)
|
||||
|
||||
type chacha20key []byte
|
||||
|
||||
func (k chacha20key) IVSize() int {
|
||||
return chacha.NonceSize
|
||||
}
|
||||
func (k chacha20key) Encrypter(iv []byte) cipher.Stream {
|
||||
c, _ := chacha.NewChaCha20(iv, k)
|
||||
return c
|
||||
}
|
||||
func (k chacha20key) Decrypter(iv []byte) cipher.Stream {
|
||||
return k.Encrypter(iv)
|
||||
}
|
||||
func ChaCha20(key []byte) (Cipher, error) {
|
||||
return chacha20key(key), nil
|
||||
}
|
||||
@@ -118,6 +118,10 @@ func ServerHandshake(rw net.Conn, authenticator auth.Authenticator) (addr Addr,
|
||||
return
|
||||
}
|
||||
|
||||
if nmethods == 1 && buf[0] == 0x02 /* will use password */ && authenticator == nil {
|
||||
authenticator = auth.AlwaysValid
|
||||
}
|
||||
|
||||
// write VER METHOD
|
||||
if authenticator != nil {
|
||||
if _, err = rw.Write([]byte{5, 2}); err != nil {
|
||||
|
||||
@@ -129,7 +129,7 @@ func (c *httpConn) Write(b []byte) (int, error) {
|
||||
buf.Write(b)
|
||||
_, err := c.Conn.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
return 0, err
|
||||
}
|
||||
c.hasSentHeader = true
|
||||
return bLength, nil
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SessionStatus = byte
|
||||
@@ -37,7 +38,6 @@ type Mux struct {
|
||||
id [2]byte
|
||||
length [2]byte
|
||||
status [2]byte
|
||||
otb []byte
|
||||
remain int
|
||||
}
|
||||
|
||||
@@ -104,14 +104,8 @@ func (m *Mux) Read(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
func (m *Mux) Write(b []byte) (int, error) {
|
||||
if m.otb != nil {
|
||||
// create a sub connection
|
||||
if _, err := m.Conn.Write(m.otb); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
m.otb = nil
|
||||
}
|
||||
m.buf.Reset()
|
||||
defer m.buf.Reset() // reset must after write (keep the data fill in NewMux can be sent)
|
||||
|
||||
binary.Write(&m.buf, binary.BigEndian, uint16(4))
|
||||
m.buf.Write(m.id[:])
|
||||
m.buf.WriteByte(SessionStatusKeep)
|
||||
@@ -123,15 +117,26 @@ func (m *Mux) Write(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
func (m *Mux) Close() error {
|
||||
_, err := m.Conn.Write([]byte{0x0, 0x4, m.id[0], m.id[1], SessionStatusEnd, OptionNone})
|
||||
if err != nil {
|
||||
return err
|
||||
errChan := make(chan error, 1)
|
||||
t := time.AfterFunc(time.Second, func() { // maybe conn write too slowly, force close underlay conn after one second
|
||||
errChan <- m.Conn.Close()
|
||||
})
|
||||
_, _ = m.Conn.Write([]byte{0x0, 0x4, m.id[0], m.id[1], SessionStatusEnd, OptionNone}) // ignore session end frame write error
|
||||
if !t.Stop() {
|
||||
// Stop does not wait for f to complete before returning, so we used a chan to know whether f is completed
|
||||
return <-errChan
|
||||
}
|
||||
return m.Conn.Close()
|
||||
}
|
||||
|
||||
func NewMux(conn net.Conn, option MuxOption) *Mux {
|
||||
buf := &bytes.Buffer{}
|
||||
mux := &Mux{
|
||||
Conn: conn,
|
||||
id: option.ID,
|
||||
}
|
||||
|
||||
// create a sub connection (in buf)
|
||||
buf := &mux.buf
|
||||
|
||||
// fill empty length
|
||||
buf.Write([]byte{0x0, 0x0})
|
||||
@@ -165,9 +170,5 @@ func NewMux(conn net.Conn, option MuxOption) *Mux {
|
||||
metadata := buf.Bytes()
|
||||
binary.BigEndian.PutUint16(metadata[:2], uint16(len(metadata)-2))
|
||||
|
||||
return &Mux{
|
||||
Conn: conn,
|
||||
id: option.ID,
|
||||
otb: metadata,
|
||||
}
|
||||
return mux
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TunnelMode int
|
||||
type TunnelMode int32
|
||||
|
||||
// ModeMapping is a mapping for Mode enum
|
||||
var ModeMapping = map[string]TunnelMode{
|
||||
@@ -21,30 +20,6 @@ const (
|
||||
Direct
|
||||
)
|
||||
|
||||
// UnmarshalYAML unserialize Mode with yaml
|
||||
func (m *TunnelMode) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var tp string
|
||||
unmarshal(&tp)
|
||||
mode, exist := ModeMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid mode")
|
||||
}
|
||||
*m = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON unserialize Mode
|
||||
func (m *TunnelMode) UnmarshalJSON(data []byte) error {
|
||||
var tp string
|
||||
json.Unmarshal(data, &tp)
|
||||
mode, exist := ModeMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid mode")
|
||||
}
|
||||
*m = mode
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalText unserialize Mode
|
||||
func (m *TunnelMode) UnmarshalText(data []byte) error {
|
||||
mode, exist := ModeMapping[strings.ToLower(string(data))]
|
||||
@@ -55,16 +30,6 @@ func (m *TunnelMode) UnmarshalText(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML serialize TunnelMode with yaml
|
||||
func (m TunnelMode) MarshalYAML() (any, error) {
|
||||
return m.String(), nil
|
||||
}
|
||||
|
||||
// MarshalJSON serialize Mode
|
||||
func (m TunnelMode) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(m.String())
|
||||
}
|
||||
|
||||
// MarshalText serialize Mode
|
||||
func (m TunnelMode) MarshalText() ([]byte, error) {
|
||||
return []byte(m.String()), nil
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type TunnelStatus int
|
||||
type TunnelStatus int32
|
||||
|
||||
// StatusMapping is a mapping for Status enum
|
||||
var StatusMapping = map[string]TunnelStatus{
|
||||
@@ -22,30 +20,6 @@ const (
|
||||
Running
|
||||
)
|
||||
|
||||
// UnmarshalYAML unserialize Status with yaml
|
||||
func (s *TunnelStatus) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var tp string
|
||||
unmarshal(&tp)
|
||||
status, exist := StatusMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid status")
|
||||
}
|
||||
*s = status
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON unserialize Status
|
||||
func (s *TunnelStatus) UnmarshalJSON(data []byte) error {
|
||||
var tp string
|
||||
json.Unmarshal(data, &tp)
|
||||
status, exist := StatusMapping[strings.ToLower(tp)]
|
||||
if !exist {
|
||||
return errors.New("invalid status")
|
||||
}
|
||||
*s = status
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalText unserialize Status
|
||||
func (s *TunnelStatus) UnmarshalText(data []byte) error {
|
||||
status, exist := StatusMapping[strings.ToLower(string(data))]
|
||||
@@ -56,16 +30,6 @@ func (s *TunnelStatus) UnmarshalText(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML serialize TunnelMode with yaml
|
||||
func (s TunnelStatus) MarshalYAML() (any, error) {
|
||||
return s.String(), nil
|
||||
}
|
||||
|
||||
// MarshalJSON serialize Status
|
||||
func (s TunnelStatus) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(s.String())
|
||||
}
|
||||
|
||||
// MarshalText serialize Status
|
||||
func (s TunnelStatus) MarshalText() ([]byte, error) {
|
||||
return []byte(s.String()), nil
|
||||
@@ -83,25 +47,3 @@ func (s TunnelStatus) String() string {
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type AtomicStatus struct {
|
||||
value atomic.Int32
|
||||
}
|
||||
|
||||
func (a *AtomicStatus) Store(s TunnelStatus) {
|
||||
a.value.Store(int32(s))
|
||||
}
|
||||
|
||||
func (a *AtomicStatus) Load() TunnelStatus {
|
||||
return TunnelStatus(a.value.Load())
|
||||
}
|
||||
|
||||
func (a *AtomicStatus) String() string {
|
||||
return a.Load().String()
|
||||
}
|
||||
|
||||
func newAtomicStatus(s TunnelStatus) *AtomicStatus {
|
||||
a := &AtomicStatus{}
|
||||
a.Store(s)
|
||||
return a
|
||||
}
|
||||
|
||||
132
tunnel/tunnel.go
132
tunnel/tunnel.go
@@ -12,6 +12,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/atomic"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/loopback"
|
||||
@@ -34,7 +35,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
status = newAtomicStatus(Suspend)
|
||||
status = atomic.NewInt32Enum(Suspend)
|
||||
udpInit sync.Once
|
||||
udpQueues []chan C.PacketAdapter
|
||||
natTable = nat.New()
|
||||
@@ -58,7 +59,7 @@ var (
|
||||
// default timeout for UDP session
|
||||
udpTimeout = 60 * time.Second
|
||||
|
||||
findProcessMode P.FindProcessMode
|
||||
findProcessMode = atomic.NewInt32Enum(P.FindProcessStrict)
|
||||
|
||||
fakeIPRange netip.Prefix
|
||||
|
||||
@@ -273,13 +274,13 @@ func SetMode(m TunnelMode) {
|
||||
}
|
||||
|
||||
func FindProcessMode() P.FindProcessMode {
|
||||
return findProcessMode
|
||||
return findProcessMode.Load()
|
||||
}
|
||||
|
||||
// SetFindProcessMode replace SetAlwaysFindProcess
|
||||
// always find process info if legacyAlways = true or mode.Always() = true, may be increase many memory
|
||||
func SetFindProcessMode(mode P.FindProcessMode) {
|
||||
findProcessMode = mode
|
||||
findProcessMode.Store(mode)
|
||||
}
|
||||
|
||||
func isHandle(t C.Type) bool {
|
||||
@@ -337,6 +338,68 @@ func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err erro
|
||||
}
|
||||
return
|
||||
}
|
||||
var (
|
||||
resolved bool
|
||||
attemptProcessLookup = metadata.Type != C.INNER
|
||||
)
|
||||
|
||||
if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok {
|
||||
metadata.DstIP, _ = node.RandIP()
|
||||
resolved = true
|
||||
}
|
||||
|
||||
helper := C.RuleMatchHelper{
|
||||
ResolveIP: func() {
|
||||
if !resolved && metadata.Host != "" && !metadata.Resolved() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)
|
||||
defer cancel()
|
||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
||||
if err != nil {
|
||||
log.Debugln("[DNS] resolve %s error: %s", metadata.Host, err.Error())
|
||||
} else {
|
||||
log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String())
|
||||
metadata.DstIP = ip
|
||||
}
|
||||
resolved = true
|
||||
}
|
||||
},
|
||||
FindProcess: func() {
|
||||
if attemptProcessLookup {
|
||||
attemptProcessLookup = false
|
||||
if !features.CMFA {
|
||||
// normal check for process
|
||||
uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort))
|
||||
if err != nil {
|
||||
log.Debugln("[Process] find process error for %s: %v", metadata.String(), err)
|
||||
} else {
|
||||
metadata.Process = filepath.Base(path)
|
||||
metadata.ProcessPath = path
|
||||
metadata.Uid = uid
|
||||
|
||||
if pkg, err := P.FindPackageName(metadata); err == nil { // for android (not CMFA) package names
|
||||
metadata.Process = pkg
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// check package names
|
||||
pkg, err := P.FindPackageName(metadata)
|
||||
if err != nil {
|
||||
log.Debugln("[Process] find process error for %s: %v", metadata.String(), err)
|
||||
} else {
|
||||
metadata.Process = pkg
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
switch FindProcessMode() {
|
||||
case P.FindProcessAlways:
|
||||
helper.FindProcess()
|
||||
helper.FindProcess = nil
|
||||
case P.FindProcessOff:
|
||||
helper.FindProcess = nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case Direct:
|
||||
@@ -345,7 +408,7 @@ func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err erro
|
||||
proxy = proxies["GLOBAL"]
|
||||
// Rule
|
||||
default:
|
||||
proxy, rule, err = match(metadata)
|
||||
proxy, rule, err = match(metadata, helper)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -590,67 +653,12 @@ func logMetadata(metadata *C.Metadata, rule C.Rule, remoteConn C.Connection) {
|
||||
}
|
||||
}
|
||||
|
||||
func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool {
|
||||
return rule.ShouldResolveIP() && metadata.Host != "" && !metadata.DstIP.IsValid()
|
||||
}
|
||||
|
||||
func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
|
||||
func match(metadata *C.Metadata, helper C.RuleMatchHelper) (C.Proxy, C.Rule, error) {
|
||||
configMux.RLock()
|
||||
defer configMux.RUnlock()
|
||||
var (
|
||||
resolved bool
|
||||
attemptProcessLookup = metadata.Type != C.INNER
|
||||
)
|
||||
|
||||
if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok {
|
||||
metadata.DstIP, _ = node.RandIP()
|
||||
resolved = true
|
||||
}
|
||||
|
||||
for _, rule := range getRules(metadata) {
|
||||
if !resolved && shouldResolveIP(rule, metadata) {
|
||||
func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)
|
||||
defer cancel()
|
||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
||||
if err != nil {
|
||||
log.Debugln("[DNS] resolve %s error: %s", metadata.Host, err.Error())
|
||||
} else {
|
||||
log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String())
|
||||
metadata.DstIP = ip
|
||||
}
|
||||
resolved = true
|
||||
}()
|
||||
}
|
||||
|
||||
if attemptProcessLookup && !findProcessMode.Off() && (findProcessMode.Always() || rule.ShouldFindProcess()) {
|
||||
attemptProcessLookup = false
|
||||
if !features.CMFA {
|
||||
// normal check for process
|
||||
uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort))
|
||||
if err != nil {
|
||||
log.Debugln("[Process] find process error for %s: %v", metadata.String(), err)
|
||||
} else {
|
||||
metadata.Process = filepath.Base(path)
|
||||
metadata.ProcessPath = path
|
||||
metadata.Uid = uid
|
||||
|
||||
if pkg, err := P.FindPackageName(metadata); err == nil { // for android (not CMFA) package names
|
||||
metadata.Process = pkg
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// check package names
|
||||
pkg, err := P.FindPackageName(metadata)
|
||||
if err != nil {
|
||||
log.Debugln("[Process] find process error for %s: %v", metadata.String(), err)
|
||||
} else {
|
||||
metadata.Process = pkg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matched, ada := rule.Match(metadata); matched {
|
||||
if matched, ada := rule.Match(metadata, helper); matched {
|
||||
adapter, ok := proxies[ada]
|
||||
if !ok {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user