mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-07 14:28:10 +03:00
Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aabbbeb0fa | ||
|
|
787aa7677b | ||
|
|
d878fc83f8 | ||
|
|
ee2b2c5ab6 | ||
|
|
81d993f49d | ||
|
|
4c3842711d | ||
|
|
ab69985fcc | ||
|
|
56bb63668c | ||
|
|
359a28f876 | ||
|
|
da9ba693cb | ||
|
|
5488b86c67 | ||
|
|
a3c054a54c | ||
|
|
1bdb488c9e | ||
|
|
d38ba9d507 | ||
|
|
9d9eaf399f | ||
|
|
eb32d166af | ||
|
|
1dbafe629a | ||
|
|
c42deab55c | ||
|
|
906d49a271 | ||
|
|
4192ca0827 |
@@ -32,9 +32,12 @@ RUN echo '{}' >/tmp/usr/local/etc/xray/03_routing.json
|
|||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/07_transport.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/07_stats.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/08_stats.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/08_fakedns.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/09_reverse.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/09_metrics.json
|
||||||
|
RUN echo '{}' >/tmp/usr/local/etc/xray/10_observatory.json
|
||||||
|
RUN echo '{}' >/tmp/usr/local/etc/xray/11_geodata.json
|
||||||
|
RUN echo '{}' >/tmp/usr/local/etc/xray/99_version.json
|
||||||
|
|
||||||
# Create log files
|
# Create log files
|
||||||
RUN mkdir -p /tmp/var/log/xray && touch \
|
RUN mkdir -p /tmp/var/log/xray && touch \
|
||||||
|
|||||||
@@ -32,9 +32,12 @@ RUN echo '{}' >/tmp/usr/local/etc/xray/03_routing.json
|
|||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/07_transport.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/07_stats.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/08_stats.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/08_fakedns.json
|
||||||
RUN echo '{}' >/tmp/usr/local/etc/xray/09_reverse.json
|
RUN echo '{}' >/tmp/usr/local/etc/xray/09_metrics.json
|
||||||
|
RUN echo '{}' >/tmp/usr/local/etc/xray/10_observatory.json
|
||||||
|
RUN echo '{}' >/tmp/usr/local/etc/xray/11_geodata.json
|
||||||
|
RUN echo '{}' >/tmp/usr/local/etc/xray/99_version.json
|
||||||
|
|
||||||
# Create log files
|
# Create log files
|
||||||
RUN mkdir -p /tmp/var/log/xray && touch \
|
RUN mkdir -p /tmp/var/log/xray && touch \
|
||||||
|
|||||||
@@ -73,7 +73,6 @@
|
|||||||
- [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool), [VPainLess](https://github.com/vpainless/vpainless)
|
- [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool), [VPainLess](https://github.com/vpainless/vpainless)
|
||||||
- [v2ray-agent](https://github.com/mack-a/v2ray-agent), [Xray_onekey](https://github.com/wulabing/Xray_onekey), [ProxySU](https://github.com/proxysu/ProxySU)
|
- [v2ray-agent](https://github.com/mack-a/v2ray-agent), [Xray_onekey](https://github.com/wulabing/Xray_onekey), [ProxySU](https://github.com/proxysu/ProxySU)
|
||||||
- Magisk
|
- Magisk
|
||||||
- [Xray4Magisk](https://github.com/Asterisk4Magisk/Xray4Magisk)
|
|
||||||
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
|
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
|
||||||
- Homebrew
|
- Homebrew
|
||||||
- `brew install xray`
|
- `brew install xray`
|
||||||
@@ -120,6 +119,7 @@
|
|||||||
- [XrayFA](https://github.com/Q7DF1/XrayFA)
|
- [XrayFA](https://github.com/Q7DF1/XrayFA)
|
||||||
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
||||||
- [OneXray](https://github.com/OneXray/OneXray)
|
- [OneXray](https://github.com/OneXray/OneXray)
|
||||||
|
- [AsteriskNG](https://github.com/Asterisk4Magisk/AsteriskNG)
|
||||||
- iOS & macOS arm64 & tvOS
|
- iOS & macOS arm64 & tvOS
|
||||||
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973) | [Happ tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274)
|
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973) | [Happ tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274)
|
||||||
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
||||||
|
|||||||
+2
-68
@@ -5,18 +5,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
go_errors "errors"
|
go_errors "errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/geodata"
|
"github.com/xtls/xray-core/common/geodata"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/utils"
|
||||||
"github.com/xtls/xray-core/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -223,7 +221,7 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.checkSystem {
|
if s.checkSystem {
|
||||||
supportIPv4, supportIPv6 := checkRoutes()
|
supportIPv4, supportIPv6 := utils.CheckRoutes()
|
||||||
option.IPv4Enable = option.IPv4Enable && supportIPv4
|
option.IPv4Enable = option.IPv4Enable && supportIPv4
|
||||||
option.IPv6Enable = option.IPv6Enable && supportIPv6
|
option.IPv6Enable = option.IPv6Enable && supportIPv6
|
||||||
} else {
|
} else {
|
||||||
@@ -539,67 +537,3 @@ func init() {
|
|||||||
return New(ctx, config.(*Config))
|
return New(ctx, config.(*Config))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func probeRoutes() (ipv4 bool, ipv6 bool) {
|
|
||||||
if conn, err := net.Dial("udp4", "192.33.4.12:53"); err == nil {
|
|
||||||
ipv4 = true
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
if conn, err := net.Dial("udp6", "[2001:500:2::c]:53"); err == nil {
|
|
||||||
ipv6 = true
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var routeCache struct {
|
|
||||||
sync.Once
|
|
||||||
sync.RWMutex
|
|
||||||
expire time.Time
|
|
||||||
ipv4, ipv6 bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRoutes() (bool, bool) {
|
|
||||||
if !isGUIPlatform {
|
|
||||||
routeCache.Once.Do(func() {
|
|
||||||
routeCache.ipv4, routeCache.ipv6 = probeRoutes()
|
|
||||||
})
|
|
||||||
return routeCache.ipv4, routeCache.ipv6
|
|
||||||
}
|
|
||||||
|
|
||||||
routeCache.RWMutex.RLock()
|
|
||||||
now := time.Now()
|
|
||||||
if routeCache.expire.After(now) {
|
|
||||||
routeCache.RWMutex.RUnlock()
|
|
||||||
return routeCache.ipv4, routeCache.ipv6
|
|
||||||
}
|
|
||||||
routeCache.RWMutex.RUnlock()
|
|
||||||
|
|
||||||
routeCache.RWMutex.Lock()
|
|
||||||
defer routeCache.RWMutex.Unlock()
|
|
||||||
|
|
||||||
now = time.Now()
|
|
||||||
if routeCache.expire.After(now) { // double-check
|
|
||||||
return routeCache.ipv4, routeCache.ipv6
|
|
||||||
}
|
|
||||||
routeCache.ipv4, routeCache.ipv6 = probeRoutes() // ~2ms
|
|
||||||
routeCache.expire = now.Add(100 * time.Millisecond) // ttl
|
|
||||||
return routeCache.ipv4, routeCache.ipv6
|
|
||||||
}
|
|
||||||
|
|
||||||
var isGUIPlatform = detectGUIPlatform()
|
|
||||||
|
|
||||||
func detectGUIPlatform() bool {
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "android", "ios", "windows", "darwin":
|
|
||||||
return true
|
|
||||||
case "linux", "freebsd", "openbsd":
|
|
||||||
if t := os.Getenv("XDG_SESSION_TYPE"); t == "wayland" || t == "x11" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/geodata"
|
"github.com/xtls/xray-core/common/geodata"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/utils"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
@@ -166,7 +167,7 @@ func (c *Client) Name() string {
|
|||||||
// QueryIP sends DNS query to the name server with the client's IP.
|
// QueryIP sends DNS query to the name server with the client's IP.
|
||||||
func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
|
func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
|
||||||
if c.checkSystem {
|
if c.checkSystem {
|
||||||
supportIPv4, supportIPv6 := checkRoutes()
|
supportIPv4, supportIPv6 := utils.CheckRoutes()
|
||||||
option.IPv4Enable = option.IPv4Enable && supportIPv4
|
option.IPv4Enable = option.IPv4Enable && supportIPv4
|
||||||
option.IPv6Enable = option.IPv6Enable && supportIPv6
|
option.IPv6Enable = option.IPv6Enable && supportIPv6
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -102,6 +102,16 @@ func (h *HealthPing) StartScheduler(selector func() ([]string, error)) {
|
|||||||
h.Check(tags)
|
h.Check(tags)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// init run to get a fast check result
|
||||||
|
go func() {
|
||||||
|
tags, err := selector()
|
||||||
|
if err != nil {
|
||||||
|
errors.LogWarning(h.ctx, "error select outbounds for initial health check: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.Check(tags)
|
||||||
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package router
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math"
|
"math"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ func (s *LeastLoadStrategy) PickOutbound(candidates []string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LeastLoadStrategy) pickOutbounds(candidates []string) []*node {
|
func (s *LeastLoadStrategy) pickOutbounds(candidates []string) []*node {
|
||||||
qualified := s.getNodes(candidates, time.Duration(s.settings.MaxRTT))
|
qualified := s.getNodes(candidates)
|
||||||
selects := s.selectLeastLoad(qualified)
|
selects := s.selectLeastLoad(qualified)
|
||||||
return selects
|
return selects
|
||||||
}
|
}
|
||||||
@@ -138,7 +139,7 @@ func (s *LeastLoadStrategy) selectLeastLoad(nodes []*node) []*node {
|
|||||||
return nodes[:count]
|
return nodes[:count]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration) []*node {
|
func (s *LeastLoadStrategy) getNodes(candidates []string) []*node {
|
||||||
if s.observer == nil {
|
if s.observer == nil {
|
||||||
errors.LogError(s.ctx, "observer is nil")
|
errors.LogError(s.ctx, "observer is nil")
|
||||||
return make([]*node, 0)
|
return make([]*node, 0)
|
||||||
@@ -151,12 +152,10 @@ func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration)
|
|||||||
|
|
||||||
results := observeResult.(*observatory.ObservationResult)
|
results := observeResult.(*observatory.ObservationResult)
|
||||||
|
|
||||||
outboundlist := outboundList(candidates)
|
|
||||||
|
|
||||||
var ret []*node
|
var ret []*node
|
||||||
|
|
||||||
for _, v := range results.Status {
|
for _, v := range results.Status {
|
||||||
if v.Alive && (v.Delay < maxRTT.Milliseconds() || maxRTT == 0) && outboundlist.contains(v.OutboundTag) {
|
if s.shouldSelectNode(v, candidates) {
|
||||||
record := &node{
|
record := &node{
|
||||||
Tag: v.OutboundTag,
|
Tag: v.OutboundTag,
|
||||||
CountAll: 1,
|
CountAll: 1,
|
||||||
@@ -172,8 +171,8 @@ func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration)
|
|||||||
record.RTTDeviationCost = time.Duration(s.costs.Apply(v.OutboundTag, float64(v.HealthPing.Deviation)))
|
record.RTTDeviationCost = time.Duration(s.costs.Apply(v.OutboundTag, float64(v.HealthPing.Deviation)))
|
||||||
record.CountAll = int(v.HealthPing.All)
|
record.CountAll = int(v.HealthPing.All)
|
||||||
record.CountFail = int(v.HealthPing.Fail)
|
record.CountFail = int(v.HealthPing.Fail)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = append(ret, record)
|
ret = append(ret, record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,6 +181,23 @@ func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration)
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *LeastLoadStrategy) shouldSelectNode(v *observatory.OutboundStatus, candidates []string) bool {
|
||||||
|
maxRTT := time.Duration(s.settings.MaxRTT)
|
||||||
|
if !v.Alive {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if maxRTT != 0 && v.Delay >= maxRTT.Milliseconds() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !slices.Contains(candidates, v.OutboundTag) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v.HealthPing != nil && v.HealthPing.All > 0 && s.settings.Tolerance > 0 && float64(v.HealthPing.Fail)/float64(v.HealthPing.All) > float64(s.settings.Tolerance) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func leastloadSort(nodes []*node) {
|
func leastloadSort(nodes []*node) {
|
||||||
sort.Slice(nodes, func(i, j int) bool {
|
sort.Slice(nodes, func(i, j int) bool {
|
||||||
left := nodes[i]
|
left := nodes[i]
|
||||||
|
|||||||
+3
-47
@@ -7,60 +7,16 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/utils"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
routing_session "github.com/xtls/xray-core/features/routing/session"
|
routing_session "github.com/xtls/xray-core/features/routing/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseURL splits a webhook URL into an HTTP URL and an optional Unix socket
|
|
||||||
// path. For regular http/https URLs the input is returned unchanged with an
|
|
||||||
// empty socketPath. For Unix sockets the format is:
|
|
||||||
//
|
|
||||||
// /path/to/socket.sock:/http/path
|
|
||||||
// @abstract:/http/path
|
|
||||||
// @@padded:/http/path
|
|
||||||
//
|
|
||||||
// The :/ separator after the socket path delimits the HTTP request path.
|
|
||||||
// If omitted, "/" is used.
|
|
||||||
func parseURL(raw string) (httpURL, socketPath string) {
|
|
||||||
if len(raw) == 0 || (!filepath.IsAbs(raw) && raw[0] != '@') {
|
|
||||||
return raw, ""
|
|
||||||
}
|
|
||||||
if idx := strings.Index(raw, ":/"); idx >= 0 {
|
|
||||||
return "http://localhost" + raw[idx+1:], raw[:idx]
|
|
||||||
}
|
|
||||||
return "http://localhost/", raw
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolveSocketPath applies platform-specific transformations to a Unix
|
|
||||||
// socket path, matching the behaviour of the listen side in
|
|
||||||
// transport/internet/system_listener.go.
|
|
||||||
//
|
|
||||||
// For abstract sockets (prefix @) on Linux/Android:
|
|
||||||
// - single @ — used as-is (lock-free abstract socket)
|
|
||||||
// - double @@ — stripped to single @ and padded to
|
|
||||||
// syscall.RawSockaddrUnix{}.Path length (HAProxy compat)
|
|
||||||
func resolveSocketPath(path string) string {
|
|
||||||
if len(path) == 0 || path[0] != '@' {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
if runtime.GOOS != "linux" && runtime.GOOS != "android" {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
if len(path) > 1 && path[1] == '@' {
|
|
||||||
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path))
|
|
||||||
copy(fullAddr, path[1:])
|
|
||||||
return string(fullAddr)
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
func ptr[T any](v T) *T { return &v }
|
func ptr[T any](v T) *T { return &v }
|
||||||
|
|
||||||
@@ -96,7 +52,7 @@ func NewWebhookNotifier(cfg *WebhookConfig) (*WebhookNotifier, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
httpURL, socketPath := parseURL(cfg.Url)
|
httpURL, socketPath := utils.SplitHTTPUnixURL(cfg.Url)
|
||||||
h := &WebhookNotifier{
|
h := &WebhookNotifier{
|
||||||
url: httpURL,
|
url: httpURL,
|
||||||
deduplication: cfg.Deduplication,
|
deduplication: cfg.Deduplication,
|
||||||
@@ -107,7 +63,7 @@ func NewWebhookNotifier(cfg *WebhookConfig) (*WebhookNotifier, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if socketPath != "" {
|
if socketPath != "" {
|
||||||
dialAddr := resolveSocketPath(socketPath)
|
dialAddr := utils.ResolveSocketPath(socketPath)
|
||||||
h.client.Transport = &http.Transport{
|
h.client.Transport = &http.Transport{
|
||||||
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
var d net.Dialer
|
var d net.Dialer
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/geodata/strmatcher"
|
"github.com/xtls/xray-core/common/geodata/strmatcher"
|
||||||
|
"github.com/xtls/xray-core/common/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DomainMatcher interface {
|
type DomainMatcher interface {
|
||||||
@@ -25,7 +26,7 @@ type DomainMatcherFactory interface {
|
|||||||
|
|
||||||
type MphDomainMatcherFactory struct {
|
type MphDomainMatcherFactory struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
shared map[string]strmatcher.MatcherGroup // TODO: cleanup
|
shared *utils.WeakCacheMap[string, strmatcher.MphValueMatcher]
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildDomainRulesKey(rules []*DomainRule) string {
|
func buildDomainRulesKey(rules []*DomainRule) string {
|
||||||
@@ -65,7 +66,7 @@ func (f *MphDomainMatcherFactory) BuildMatcher(rules []*DomainRule) (DomainMatch
|
|||||||
if key != "" {
|
if key != "" {
|
||||||
f.Lock()
|
f.Lock()
|
||||||
defer f.Unlock()
|
defer f.Unlock()
|
||||||
if g := f.shared[key]; g != nil {
|
if g, ok := f.shared.Load(key); ok {
|
||||||
errors.LogDebug(context.Background(), "geodata mph domain matcher cache HIT for ", len(rules), " rules")
|
errors.LogDebug(context.Background(), "geodata mph domain matcher cache HIT for ", len(rules), " rules")
|
||||||
return g, nil
|
return g, nil
|
||||||
}
|
}
|
||||||
@@ -102,14 +103,14 @@ func (f *MphDomainMatcherFactory) BuildMatcher(rules []*DomainRule) (DomainMatch
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if key != "" {
|
if key != "" {
|
||||||
f.shared[key] = g
|
f.shared.Store(key, g)
|
||||||
}
|
}
|
||||||
return g, nil
|
return g, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompactDomainMatcherFactory struct {
|
type CompactDomainMatcherFactory struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
shared map[string]strmatcher.MatcherSet // TODO: cleanup
|
shared *utils.WeakCacheMap[string, strmatcher.LinearAnyMatcher]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *CompactDomainMatcherFactory) getOrCreateFrom(rule *GeoSiteRule) (strmatcher.MatcherSet, error) {
|
func (f *CompactDomainMatcherFactory) getOrCreateFrom(rule *GeoSiteRule) (strmatcher.MatcherSet, error) {
|
||||||
@@ -118,7 +119,7 @@ func (f *CompactDomainMatcherFactory) getOrCreateFrom(rule *GeoSiteRule) (strmat
|
|||||||
f.Lock()
|
f.Lock()
|
||||||
defer f.Unlock()
|
defer f.Unlock()
|
||||||
|
|
||||||
if s := f.shared[key]; s != nil {
|
if s, ok := f.shared.Load(key); ok {
|
||||||
errors.LogDebug(context.Background(), "geodata geosite matcher cache HIT ", key)
|
errors.LogDebug(context.Background(), "geodata geosite matcher cache HIT ", key)
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
@@ -138,7 +139,7 @@ func (f *CompactDomainMatcherFactory) getOrCreateFrom(rule *GeoSiteRule) (strmat
|
|||||||
}
|
}
|
||||||
s.Add(m)
|
s.Add(m)
|
||||||
}
|
}
|
||||||
f.shared[key] = s
|
f.shared.Store(key, s)
|
||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,8 +231,8 @@ func parseDomain(d *Domain) (strmatcher.Matcher, error) {
|
|||||||
func newDomainMatcherFactory() DomainMatcherFactory {
|
func newDomainMatcherFactory() DomainMatcherFactory {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "ios", "android":
|
case "ios", "android":
|
||||||
return &CompactDomainMatcherFactory{shared: make(map[string]strmatcher.MatcherSet)}
|
return &CompactDomainMatcherFactory{shared: utils.NewWeakCacheMap[string, strmatcher.LinearAnyMatcher]()}
|
||||||
default:
|
default:
|
||||||
return &MphDomainMatcherFactory{shared: make(map[string]strmatcher.MatcherGroup)}
|
return &MphDomainMatcherFactory{shared: utils.NewWeakCacheMap[string, strmatcher.MphValueMatcher]()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/geodata/strmatcher"
|
"github.com/xtls/xray-core/common/geodata/strmatcher"
|
||||||
|
"github.com/xtls/xray-core/common/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCompactDomainMatcher_PreservesCustomRuleIndices(t *testing.T) {
|
func TestCompactDomainMatcher_PreservesCustomRuleIndices(t *testing.T) {
|
||||||
factory := &CompactDomainMatcherFactory{shared: make(map[string]strmatcher.MatcherSet)}
|
factory := &CompactDomainMatcherFactory{shared: utils.NewWeakCacheMap[string, strmatcher.LinearAnyMatcher]()}
|
||||||
matcher, err := factory.BuildMatcher([]*DomainRule{
|
matcher, err := factory.BuildMatcher([]*DomainRule{
|
||||||
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Full, Value: "example.com"}}},
|
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Full, Value: "example.com"}}},
|
||||||
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Domain, Value: "example.com"}}},
|
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Domain, Value: "example.com"}}},
|
||||||
@@ -31,7 +32,7 @@ func TestCompactDomainMatcher_PreservesCustomRuleIndices(t *testing.T) {
|
|||||||
func TestCompactDomainMatcher_PreservesMixedRuleIndices(t *testing.T) {
|
func TestCompactDomainMatcher_PreservesMixedRuleIndices(t *testing.T) {
|
||||||
t.Setenv("xray.location.asset", filepath.Join("..", "..", "resources"))
|
t.Setenv("xray.location.asset", filepath.Join("..", "..", "resources"))
|
||||||
|
|
||||||
factory := &CompactDomainMatcherFactory{shared: make(map[string]strmatcher.MatcherSet)}
|
factory := &CompactDomainMatcherFactory{shared: utils.NewWeakCacheMap[string, strmatcher.LinearAnyMatcher]()}
|
||||||
matcher, err := factory.BuildMatcher([]*DomainRule{
|
matcher, err := factory.BuildMatcher([]*DomainRule{
|
||||||
{Value: &DomainRule_Geosite{Geosite: &GeoSiteRule{File: DefaultGeoSiteDat, Code: "CN"}}},
|
{Value: &DomainRule_Geosite{Geosite: &GeoSiteRule{File: DefaultGeoSiteDat, Code: "CN"}}},
|
||||||
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Full, Value: "163.com"}}},
|
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Full, Value: "163.com"}}},
|
||||||
@@ -50,10 +51,11 @@ func TestCompactDomainMatcher_PreservesMixedRuleIndices(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMphDomainMatcher_MatchReturnsDetachedSlice(t *testing.T) {
|
func TestMphDomainMatcher_MatchReturnsDetachedSlice(t *testing.T) {
|
||||||
matcher, err := (&MphDomainMatcherFactory{shared: make(map[string]strmatcher.MatcherGroup)}).BuildMatcher([]*DomainRule{
|
matcher, err := (&MphDomainMatcherFactory{shared: utils.NewWeakCacheMap[string, strmatcher.MphValueMatcher]()}).
|
||||||
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Full, Value: "example.com"}}},
|
BuildMatcher([]*DomainRule{
|
||||||
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Domain, Value: "example.com"}}},
|
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Full, Value: "example.com"}}},
|
||||||
})
|
{Value: &DomainRule_Custom{Custom: &Domain{Type: Domain_Domain, Value: "example.com"}}},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("BuildMatcher() failed: %v", err)
|
t.Fatalf("BuildMatcher() failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/utils"
|
||||||
|
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
)
|
)
|
||||||
@@ -806,7 +807,7 @@ func (mm *HeuristicMultiIPMatcher) SetReverse(reverse bool) {
|
|||||||
|
|
||||||
type IPSetFactory struct {
|
type IPSetFactory struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
shared map[string]*IPSet // TODO: cleanup
|
shared *utils.WeakCacheMap[string, IPSet]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *IPSetFactory) GetOrCreateFromGeoIPRules(rules []*GeoIPRule) (*IPSet, error) {
|
func (f *IPSetFactory) GetOrCreateFromGeoIPRules(rules []*GeoIPRule) (*IPSet, error) {
|
||||||
@@ -815,7 +816,7 @@ func (f *IPSetFactory) GetOrCreateFromGeoIPRules(rules []*GeoIPRule) (*IPSet, er
|
|||||||
f.Lock()
|
f.Lock()
|
||||||
defer f.Unlock()
|
defer f.Unlock()
|
||||||
|
|
||||||
if ipset := f.shared[key]; ipset != nil {
|
if ipset, ok := f.shared.Load(key); ok {
|
||||||
errors.LogDebug(context.Background(), "geodata geoip matcher cache HIT ", key)
|
errors.LogDebug(context.Background(), "geodata geoip matcher cache HIT ", key)
|
||||||
return ipset, nil
|
return ipset, nil
|
||||||
}
|
}
|
||||||
@@ -835,7 +836,7 @@ func (f *IPSetFactory) GetOrCreateFromGeoIPRules(rules []*GeoIPRule) (*IPSet, er
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
f.shared[key] = ipset
|
f.shared.Store(key, ipset)
|
||||||
}
|
}
|
||||||
return ipset, err
|
return ipset, err
|
||||||
}
|
}
|
||||||
@@ -1018,5 +1019,5 @@ func buildOptimizedIPMatcher(f *IPSetFactory, rules []*IPRule) (IPMatcher, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newIPSetFactory() *IPSetFactory {
|
func newIPSetFactory() *IPSetFactory {
|
||||||
return &IPSetFactory{shared: make(map[string]*IPSet)}
|
return &IPSetFactory{shared: utils.NewWeakCacheMap[string, IPSet]()}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package singbridge
|
|||||||
import (
|
import (
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,14 +18,14 @@ func ToNetwork(network string) net.Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToDestination(socksaddr M.Socksaddr, network net.Network) net.Destination {
|
func ToDestination(socksaddr M.Socksaddr, network net.Network) (net.Destination, error) {
|
||||||
// IsFqdn() implicitly checks if the domain name is valid
|
// IsFqdn() implicitly checks if the domain name is valid
|
||||||
if socksaddr.IsFqdn() {
|
if socksaddr.IsFqdn() {
|
||||||
return net.Destination{
|
return net.Destination{
|
||||||
Network: network,
|
Network: network,
|
||||||
Address: net.DomainAddress(socksaddr.Fqdn),
|
Address: net.DomainAddress(socksaddr.Fqdn),
|
||||||
Port: net.Port(socksaddr.Port),
|
Port: net.Port(socksaddr.Port),
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsIP() implicitly checks if the IP address is valid
|
// IsIP() implicitly checks if the IP address is valid
|
||||||
@@ -33,10 +34,10 @@ func ToDestination(socksaddr M.Socksaddr, network net.Network) net.Destination {
|
|||||||
Network: network,
|
Network: network,
|
||||||
Address: net.IPAddress(socksaddr.Addr.AsSlice()),
|
Address: net.IPAddress(socksaddr.Addr.AsSlice()),
|
||||||
Port: net.Port(socksaddr.Port),
|
Port: net.Port(socksaddr.Port),
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return net.Destination{}
|
return net.Destination{}, errors.New("invalid socks address: ", socksaddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToSocksaddr(destination net.Destination) M.Socksaddr {
|
func ToSocksaddr(destination net.Destination) M.Socksaddr {
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ func NewDialer(dialer internet.Dialer) *XrayDialer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *XrayDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (d *XrayDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
return d.Dialer.Dial(ctx, ToDestination(destination, ToNetwork(network)))
|
dest, err := ToDestination(destination, ToNetwork(network))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.Dialer.Dial(ctx, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *XrayDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (d *XrayDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
@@ -43,13 +47,17 @@ func NewOutboundDialer(outbound proxy.Outbound, dialer internet.Dialer) *XrayOut
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *XrayOutboundDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (d *XrayOutboundDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
dest, err := ToDestination(destination, ToNetwork(network))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
outbounds := session.OutboundsFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if len(outbounds) == 0 {
|
if len(outbounds) == 0 {
|
||||||
outbounds = []*session.Outbound{{}}
|
outbounds = []*session.Outbound{{}}
|
||||||
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
}
|
}
|
||||||
ob := outbounds[len(outbounds)-1]
|
ob := outbounds[len(outbounds)-1]
|
||||||
ob.Target = ToDestination(destination, ToNetwork(network))
|
ob.Target = dest
|
||||||
|
|
||||||
opts := []pipe.Option{pipe.WithSizeLimit(64 * 1024)}
|
opts := []pipe.Option{pipe.WithSizeLimit(64 * 1024)}
|
||||||
uplinkReader, uplinkWriter := pipe.New(opts...)
|
uplinkReader, uplinkWriter := pipe.New(opts...)
|
||||||
|
|||||||
@@ -31,15 +31,23 @@ func NewDispatcher(dispatcher routing.Dispatcher, newErrorFunc func(values ...an
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dispatcher) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (d *Dispatcher) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
|
dest, err := ToDestination(metadata.Destination, net.Network_TCP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
xConn := NewConn(conn)
|
xConn := NewConn(conn)
|
||||||
return d.upstream.DispatchLink(ctx, ToDestination(metadata.Destination, net.Network_TCP), &transport.Link{
|
return d.upstream.DispatchLink(ctx, dest, &transport.Link{
|
||||||
Reader: xConn,
|
Reader: xConn,
|
||||||
Writer: xConn,
|
Writer: xConn,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dispatcher) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
func (d *Dispatcher) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
return d.upstream.DispatchLink(ctx, ToDestination(metadata.Destination, net.Network_UDP), &transport.Link{
|
dest, err := ToDestination(metadata.Destination, net.Network_UDP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.upstream.DispatchLink(ctx, dest, &transport.Link{
|
||||||
Reader: buf.NewPacketReader(conn.(io.Reader)),
|
Reader: buf.NewPacketReader(conn.(io.Reader)),
|
||||||
Writer: buf.NewWriter(conn.(io.Writer)),
|
Writer: buf.NewWriter(conn.(io.Writer)),
|
||||||
})
|
})
|
||||||
|
|||||||
+31
-30
@@ -10,15 +10,21 @@ import (
|
|||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/signal"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CopyPacketConn(ctx context.Context, inboundConn net.Conn, link *transport.Link, destination net.Destination, serverConn net.PacketConn) error {
|
func CopyPacketConn(ctx context.Context, inboundConn net.Conn, link *transport.Link, destination net.Destination, serverConn net.PacketConn) error {
|
||||||
|
cancel := func() {
|
||||||
|
common.Interrupt(link.Reader)
|
||||||
|
common.Interrupt(serverConn)
|
||||||
|
}
|
||||||
conn := &PacketConnWrapper{
|
conn := &PacketConnWrapper{
|
||||||
Reader: link.Reader,
|
Reader: link.Reader,
|
||||||
Writer: link.Writer,
|
Writer: link.Writer,
|
||||||
Dest: destination,
|
Dest: destination,
|
||||||
Conn: inboundConn,
|
Conn: inboundConn,
|
||||||
|
T: signal.CancelAfterInactivity(ctx, cancel, 300*time.Second),
|
||||||
}
|
}
|
||||||
return ReturnError(bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(serverConn)))
|
return ReturnError(bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(serverConn)))
|
||||||
}
|
}
|
||||||
@@ -29,11 +35,19 @@ type PacketConnWrapper struct {
|
|||||||
net.Conn
|
net.Conn
|
||||||
Dest net.Destination
|
Dest net.Destination
|
||||||
cached buf.MultiBuffer
|
cached buf.MultiBuffer
|
||||||
|
|
||||||
|
// A simple patch to avoid goroutine leak since sing infra cannot awake read block by write err
|
||||||
|
T *signal.ActivityTimer
|
||||||
}
|
}
|
||||||
|
|
||||||
// This ReadPacket implemented a timeout to avoid goroutine leak like PipeConnWrapper.Read()
|
func (w *PacketConnWrapper) ReadPacket(buffer *B.Buffer) (addr M.Socksaddr, err error) {
|
||||||
// as a temporarily solution
|
w.T.Update()
|
||||||
func (w *PacketConnWrapper) ReadPacket(buffer *B.Buffer) (M.Socksaddr, error) {
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
// uplinkonly
|
||||||
|
w.T.SetTimeout(2 * time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
if w.cached != nil {
|
if w.cached != nil {
|
||||||
mb, bb := buf.SplitFirst(w.cached)
|
mb, bb := buf.SplitFirst(w.cached)
|
||||||
if bb == nil {
|
if bb == nil {
|
||||||
@@ -51,30 +65,7 @@ func (w *PacketConnWrapper) ReadPacket(buffer *B.Buffer) (M.Socksaddr, error) {
|
|||||||
return ToSocksaddr(destination), nil
|
return ToSocksaddr(destination), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mb, err := w.ReadMultiBuffer()
|
||||||
// timeout
|
|
||||||
type readResult struct {
|
|
||||||
mb buf.MultiBuffer
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
c := make(chan readResult, 1)
|
|
||||||
go func() {
|
|
||||||
mb, err := w.ReadMultiBuffer()
|
|
||||||
c <- readResult{mb: mb, err: err}
|
|
||||||
}()
|
|
||||||
var mb buf.MultiBuffer
|
|
||||||
select {
|
|
||||||
case <-time.After(60 * time.Second):
|
|
||||||
common.Close(w.Reader)
|
|
||||||
common.Interrupt(w.Reader)
|
|
||||||
return M.Socksaddr{}, buf.ErrReadTimeout
|
|
||||||
case result := <-c:
|
|
||||||
if result.err != nil {
|
|
||||||
return M.Socksaddr{}, result.err
|
|
||||||
}
|
|
||||||
mb = result.mb
|
|
||||||
}
|
|
||||||
|
|
||||||
nb, bb := buf.SplitFirst(mb)
|
nb, bb := buf.SplitFirst(mb)
|
||||||
if bb == nil {
|
if bb == nil {
|
||||||
return M.Socksaddr{}, nil
|
return M.Socksaddr{}, nil
|
||||||
@@ -92,12 +83,22 @@ func (w *PacketConnWrapper) ReadPacket(buffer *B.Buffer) (M.Socksaddr, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *PacketConnWrapper) WritePacket(buffer *B.Buffer, destination M.Socksaddr) error {
|
func (w *PacketConnWrapper) WritePacket(buffer *B.Buffer, destination M.Socksaddr) (err error) {
|
||||||
|
w.T.Update()
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
// downlinkonly
|
||||||
|
w.T.SetTimeout(5 * time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
endpoint, err := ToDestination(destination, net.Network_UDP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
vBuf := buf.New()
|
vBuf := buf.New()
|
||||||
vBuf.Write(buffer.Bytes())
|
vBuf.Write(buffer.Bytes())
|
||||||
endpoint := ToDestination(destination, net.Network_UDP)
|
|
||||||
vBuf.UDP = &endpoint
|
vBuf.UDP = &endpoint
|
||||||
return w.Writer.WriteMultiBuffer(buf.MultiBuffer{vBuf})
|
return w.WriteMultiBuffer(buf.MultiBuffer{vBuf})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *PacketConnWrapper) Close() error {
|
func (w *PacketConnWrapper) Close() error {
|
||||||
|
|||||||
+18
-18
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
"github.com/xtls/xray-core/common/signal"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,6 +23,11 @@ func CopyConn(ctx context.Context, inboundConn net.Conn, link *transport.Link, s
|
|||||||
} else {
|
} else {
|
||||||
conn.R = &buf.BufferedReader{Reader: link.Reader}
|
conn.R = &buf.BufferedReader{Reader: link.Reader}
|
||||||
}
|
}
|
||||||
|
cancel := func() {
|
||||||
|
common.Interrupt(link.Reader)
|
||||||
|
common.Interrupt(serverConn)
|
||||||
|
}
|
||||||
|
conn.T = signal.CancelAfterInactivity(ctx, cancel, 300*time.Second)
|
||||||
return ReturnError(bufio.CopyConn(ctx, conn, serverConn))
|
return ReturnError(bufio.CopyConn(ctx, conn, serverConn))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,35 +35,27 @@ type PipeConnWrapper struct {
|
|||||||
R io.Reader
|
R io.Reader
|
||||||
W buf.Writer
|
W buf.Writer
|
||||||
net.Conn
|
net.Conn
|
||||||
|
|
||||||
|
// A simple patch to avoid goroutine leak since sing infra cannot awake read block by write err
|
||||||
|
T *signal.ActivityTimer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *PipeConnWrapper) Close() error {
|
func (w *PipeConnWrapper) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This Read implemented a timeout to avoid goroutine leak.
|
|
||||||
// as a temporarily solution
|
|
||||||
func (w *PipeConnWrapper) Read(b []byte) (n int, err error) {
|
func (w *PipeConnWrapper) Read(b []byte) (n int, err error) {
|
||||||
type readResult struct {
|
w.T.Update()
|
||||||
n int
|
n, err = w.R.Read(b)
|
||||||
err error
|
if err != nil {
|
||||||
}
|
// uplinkonly
|
||||||
c := make(chan readResult, 1)
|
w.T.SetTimeout(2 * time.Second)
|
||||||
go func() {
|
|
||||||
n, err := w.R.Read(b)
|
|
||||||
c <- readResult{n: n, err: err}
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case result := <-c:
|
|
||||||
return result.n, result.err
|
|
||||||
case <-time.After(300 * time.Second):
|
|
||||||
common.Close(w.R)
|
|
||||||
common.Interrupt(w.R)
|
|
||||||
return 0, buf.ErrReadTimeout
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *PipeConnWrapper) Write(p []byte) (n int, err error) {
|
func (w *PipeConnWrapper) Write(p []byte) (n int, err error) {
|
||||||
|
w.T.Update()
|
||||||
n = len(p)
|
n = len(p)
|
||||||
var mb buf.MultiBuffer
|
var mb buf.MultiBuffer
|
||||||
pLen := len(p)
|
pLen := len(p)
|
||||||
@@ -76,6 +74,8 @@ func (w *PipeConnWrapper) Write(p []byte) (n int, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
n = 0
|
n = 0
|
||||||
buf.ReleaseMulti(mb)
|
buf.ReleaseMulti(mb)
|
||||||
|
// downlinkonly
|
||||||
|
w.T.SetTimeout(5 * time.Second)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func probeRoutes() (ipv4 bool, ipv6 bool) {
|
||||||
|
if conn, err := net.Dial("udp4", "192.33.4.12:53"); err == nil {
|
||||||
|
ipv4 = true
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
if conn, err := net.Dial("udp6", "[2001:500:2::c]:53"); err == nil {
|
||||||
|
ipv6 = true
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var routeCache struct {
|
||||||
|
sync.Once
|
||||||
|
sync.RWMutex
|
||||||
|
expire time.Time
|
||||||
|
ipv4, ipv6 bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckRoutes() (bool, bool) {
|
||||||
|
if !isGUIPlatform {
|
||||||
|
routeCache.Once.Do(func() {
|
||||||
|
routeCache.ipv4, routeCache.ipv6 = probeRoutes()
|
||||||
|
})
|
||||||
|
return routeCache.ipv4, routeCache.ipv6
|
||||||
|
}
|
||||||
|
|
||||||
|
routeCache.RWMutex.RLock()
|
||||||
|
now := time.Now()
|
||||||
|
if routeCache.expire.After(now) {
|
||||||
|
routeCache.RWMutex.RUnlock()
|
||||||
|
return routeCache.ipv4, routeCache.ipv6
|
||||||
|
}
|
||||||
|
routeCache.RWMutex.RUnlock()
|
||||||
|
|
||||||
|
routeCache.RWMutex.Lock()
|
||||||
|
defer routeCache.RWMutex.Unlock()
|
||||||
|
|
||||||
|
now = time.Now()
|
||||||
|
if routeCache.expire.After(now) { // double-check
|
||||||
|
return routeCache.ipv4, routeCache.ipv6
|
||||||
|
}
|
||||||
|
routeCache.ipv4, routeCache.ipv6 = probeRoutes() // ~2ms
|
||||||
|
routeCache.expire = now.Add(100 * time.Millisecond) // ttl
|
||||||
|
return routeCache.ipv4, routeCache.ipv6
|
||||||
|
}
|
||||||
|
|
||||||
|
var isGUIPlatform = detectGUIPlatform()
|
||||||
|
|
||||||
|
func detectGUIPlatform() bool {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "android", "ios", "windows", "darwin":
|
||||||
|
return true
|
||||||
|
case "linux", "freebsd", "openbsd":
|
||||||
|
if t := os.Getenv("XDG_SESSION_TYPE"); t == "wayland" || t == "x11" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResolveSocketPath applies platform-specific transformations to a Unix
|
||||||
|
// socket path, matching the listen-side behaviour in
|
||||||
|
// transport/internet/system_listener.go.
|
||||||
|
//
|
||||||
|
// For abstract sockets (prefix @) on Linux/Android:
|
||||||
|
// - single @ — used as-is (lock-free abstract socket)
|
||||||
|
// - double @@ — stripped to single @ and padded to
|
||||||
|
// syscall.RawSockaddrUnix{}.Path length (HAProxy compat)
|
||||||
|
//
|
||||||
|
// Filesystem paths and abstract sockets on other platforms are returned
|
||||||
|
// unchanged.
|
||||||
|
func ResolveSocketPath(path string) string {
|
||||||
|
if len(path) == 0 || path[0] != '@' {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
if runtime.GOOS != "linux" && runtime.GOOS != "android" {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
if len(path) > 1 && path[1] == '@' {
|
||||||
|
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path))
|
||||||
|
copy(fullAddr, path[1:])
|
||||||
|
return string(fullAddr)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitHTTPUnixURL splits a target into an HTTP URL and an optional Unix
|
||||||
|
// socket path. For regular http(s) URLs the input is returned unchanged
|
||||||
|
// with an empty socketPath. For Unix sockets the format is:
|
||||||
|
//
|
||||||
|
// /path/to/socket.sock[:/http/path]
|
||||||
|
// @abstract[:/http/path]
|
||||||
|
// @@padded[:/http/path]
|
||||||
|
//
|
||||||
|
// The :/ separator delimits the socket path from the HTTP request path.
|
||||||
|
// If omitted, "/" is used.
|
||||||
|
func SplitHTTPUnixURL(raw string) (httpURL, socketPath string) {
|
||||||
|
if len(raw) == 0 || (!filepath.IsAbs(raw) && raw[0] != '@') {
|
||||||
|
return raw, ""
|
||||||
|
}
|
||||||
|
if idx := strings.Index(raw, ":/"); idx >= 0 {
|
||||||
|
return "http://localhost" + raw[idx+1:], raw[:idx]
|
||||||
|
}
|
||||||
|
return "http://localhost/", raw
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"weak"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WeakCacheMap is a map that holds weak references to values.
|
||||||
|
// Use for shared expensive objects and automatic cleanup when no longer used.
|
||||||
|
// This object can be GC and no goroutine is used for cleanup.
|
||||||
|
type WeakCacheMap[K comparable, V any] struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
m map[K]weak.Pointer[V]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWeakCacheMap[K comparable, V any]() *WeakCacheMap[K, V] {
|
||||||
|
return &WeakCacheMap[K, V]{
|
||||||
|
m: make(map[K]weak.Pointer[V]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WeakCacheMap[K, V]) Load(key K) (value *V, ok bool) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
weakPtr := c.m[key].Value()
|
||||||
|
if weakPtr != nil {
|
||||||
|
return weakPtr, true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WeakCacheMap[K, V]) Store(key K, value *V) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
weakPtr := weak.Make(value)
|
||||||
|
c.m[key] = weakPtr
|
||||||
|
runtime.AddCleanup(value, func(struct{}) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.m[key] == weakPtr {
|
||||||
|
delete(c.m, key)
|
||||||
|
}
|
||||||
|
}, struct{}{})
|
||||||
|
}
|
||||||
+1
-1
@@ -20,7 +20,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
Version_x byte = 26
|
Version_x byte = 26
|
||||||
Version_y byte = 5
|
Version_y byte = 5
|
||||||
Version_z byte = 3
|
Version_z byte = 9
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
+4
-4
@@ -54,9 +54,9 @@ func TestXrayClose(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
RewriteAddress: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(0),
|
RewritePort: uint32(0),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -66,7 +66,7 @@ func TestXrayClose(t *testing.T) {
|
|||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(0),
|
Port: uint32(0),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -21,15 +21,15 @@ require (
|
|||||||
github.com/vishvananda/netlink v1.3.1
|
github.com/vishvananda/netlink v1.3.1
|
||||||
github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f
|
github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.50.0
|
golang.org/x/crypto v0.51.0
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||||
golang.org/x/net v0.53.0
|
golang.org/x/net v0.54.0
|
||||||
golang.org/x/sync v0.20.0
|
golang.org/x/sync v0.20.0
|
||||||
golang.org/x/sys v0.43.0
|
golang.org/x/sys v0.44.0
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
||||||
golang.zx2c4.com/wireguard/windows v1.0.1
|
golang.zx2c4.com/wireguard/windows v1.0.1
|
||||||
google.golang.org/grpc v1.80.0
|
google.golang.org/grpc v1.81.1
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
||||||
h12.io/socks v1.0.3
|
h12.io/socks v1.0.3
|
||||||
@@ -46,11 +46,11 @@ require (
|
|||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.6.0 // indirect
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
github.com/vishvananda/netns v0.0.5 // indirect
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
golang.org/x/mod v0.34.0 // indirect
|
golang.org/x/mod v0.35.0 // indirect
|
||||||
golang.org/x/text v0.36.0 // indirect
|
golang.org/x/text v0.37.0 // indirect
|
||||||
golang.org/x/time v0.12.0 // indirect
|
golang.org/x/time v0.12.0 // indirect
|
||||||
golang.org/x/tools v0.43.0 // indirect
|
golang.org/x/tools v0.44.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -72,34 +72,34 @@ github.com/xtls/reality v0.0.0-20260322125925-9234c772ba8f/go.mod h1:DsJblcWDGt7
|
|||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
@@ -111,21 +111,21 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
|
||||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -137,10 +137,10 @@ golang.zx2c4.com/wireguard/windows v1.0.1 h1:eOxiDVbywPC+ZQqvdCK7x+ZwWXKbYv50TtH
|
|||||||
golang.zx2c4.com/wireguard/windows v1.0.1/go.mod h1:+fbT3FFdX4zzYDLwJh5+HPEcNN/3HyNdzhNSVsQM+zs=
|
golang.zx2c4.com/wireguard/windows v1.0.1/go.mod h1:+fbT3FFdX4zzYDLwJh5+HPEcNN/3HyNdzhNSVsQM+zs=
|
||||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
+24
-12
@@ -58,25 +58,37 @@ func (c *DNSOutboundRuleConfig) Build() (*dns.DNSRuleConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DNSOutboundConfig struct {
|
type DNSOutboundConfig struct {
|
||||||
Network Network `json:"network"`
|
RewriteNetwork Network `json:"rewriteNetwork"`
|
||||||
Address *Address `json:"address"`
|
RewriteAddress *Address `json:"rewriteAddress"`
|
||||||
Port uint16 `json:"port"`
|
RewritePort uint16 `json:"rewritePort"`
|
||||||
UserLevel uint32 `json:"userLevel"`
|
Network Network `json:"network"`
|
||||||
Rules []*DNSOutboundRuleConfig `json:"rules"`
|
Address *Address `json:"address"`
|
||||||
NonIPQuery *string `json:"nonIPQuery"` // todo: remove legacy
|
Port uint16 `json:"port"`
|
||||||
BlockTypes *[]int32 `json:"blockTypes"` // todo: remove legacy
|
UserLevel uint32 `json:"userLevel"`
|
||||||
|
Rules []*DNSOutboundRuleConfig `json:"rules"`
|
||||||
|
NonIPQuery *string `json:"nonIPQuery"` // todo: remove legacy
|
||||||
|
BlockTypes *[]int32 `json:"blockTypes"` // todo: remove legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSOutboundConfig) Build() (proto.Message, error) {
|
func (c *DNSOutboundConfig) Build() (proto.Message, error) {
|
||||||
|
if len(c.Network) > 0 {
|
||||||
|
c.RewriteNetwork = c.Network
|
||||||
|
}
|
||||||
|
if c.Address != nil {
|
||||||
|
c.RewriteAddress = c.Address
|
||||||
|
}
|
||||||
|
if c.Port != 0 {
|
||||||
|
c.RewritePort = c.Port
|
||||||
|
}
|
||||||
config := &dns.Config{
|
config := &dns.Config{
|
||||||
Server: &net.Endpoint{
|
RewriteServer: &net.Endpoint{
|
||||||
Network: c.Network.Build(),
|
Network: c.RewriteNetwork.Build(),
|
||||||
Port: uint32(c.Port),
|
Port: uint32(c.RewritePort),
|
||||||
},
|
},
|
||||||
UserLevel: c.UserLevel,
|
UserLevel: c.UserLevel,
|
||||||
}
|
}
|
||||||
if c.Address != nil {
|
if c.RewriteAddress != nil {
|
||||||
config.Server.Address = c.Address.Build()
|
config.RewriteServer.Address = c.RewriteAddress.Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: remove legacy
|
// todo: remove legacy
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &dns.Config{
|
Output: &dns.Config{
|
||||||
Server: &net.Endpoint{
|
RewriteServer: &net.Endpoint{
|
||||||
Network: net.Network_TCP,
|
Network: net.Network_TCP,
|
||||||
Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})),
|
Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})),
|
||||||
Port: 53,
|
Port: 53,
|
||||||
@@ -44,7 +44,7 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &dns.Config{
|
Output: &dns.Config{
|
||||||
Server: &net.Endpoint{},
|
RewriteServer: &net.Endpoint{},
|
||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Direct,
|
Action: dns.RuleAction_Direct,
|
||||||
@@ -84,7 +84,7 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &dns.Config{
|
Output: &dns.Config{
|
||||||
Server: &net.Endpoint{},
|
RewriteServer: &net.Endpoint{},
|
||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Reject,
|
Action: dns.RuleAction_Reject,
|
||||||
@@ -111,7 +111,7 @@ func TestDnsProxyConfig(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &dns.Config{
|
Output: &dns.Config{
|
||||||
Server: &net.Endpoint{},
|
RewriteServer: &net.Endpoint{},
|
||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Drop,
|
Action: dns.RuleAction_Drop,
|
||||||
@@ -136,7 +136,7 @@ func TestDnsProxyConfigLegacyCompatibility(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &dns.Config{
|
Output: &dns.Config{
|
||||||
Server: &net.Endpoint{},
|
RewriteServer: &net.Endpoint{},
|
||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Hijack,
|
Action: dns.RuleAction_Hijack,
|
||||||
@@ -154,7 +154,7 @@ func TestDnsProxyConfigLegacyCompatibility(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &dns.Config{
|
Output: &dns.Config{
|
||||||
Server: &net.Endpoint{},
|
RewriteServer: &net.Endpoint{},
|
||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Reject,
|
Action: dns.RuleAction_Reject,
|
||||||
@@ -177,7 +177,7 @@ func TestDnsProxyConfigLegacyCompatibility(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &dns.Config{
|
Output: &dns.Config{
|
||||||
Server: &net.Endpoint{},
|
RewriteServer: &net.Endpoint{},
|
||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Drop,
|
Action: dns.RuleAction_Drop,
|
||||||
@@ -200,7 +200,7 @@ func TestDnsProxyConfigLegacyCompatibility(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &dns.Config{
|
Output: &dns.Config{
|
||||||
Server: &net.Endpoint{},
|
RewriteServer: &net.Endpoint{},
|
||||||
Rule: []*dns.DNSRuleConfig{
|
Rule: []*dns.DNSRuleConfig{
|
||||||
{
|
{
|
||||||
Action: dns.RuleAction_Drop,
|
Action: dns.RuleAction_Drop,
|
||||||
|
|||||||
+18
-6
@@ -8,27 +8,39 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DokodemoConfig struct {
|
type DokodemoConfig struct {
|
||||||
|
AllowedNetwork *NetworkList `json:"allowedNetwork"`
|
||||||
|
RewriteAddress *Address `json:"rewriteAddress"`
|
||||||
|
RewritePort uint16 `json:"rewritePort"`
|
||||||
|
Network *NetworkList `json:"network"`
|
||||||
Address *Address `json:"address"`
|
Address *Address `json:"address"`
|
||||||
Port uint16 `json:"port"`
|
Port uint16 `json:"port"`
|
||||||
PortMap map[string]string `json:"portMap"`
|
PortMap map[string]string `json:"portMap"`
|
||||||
Network *NetworkList `json:"network"`
|
|
||||||
FollowRedirect bool `json:"followRedirect"`
|
FollowRedirect bool `json:"followRedirect"`
|
||||||
UserLevel uint32 `json:"userLevel"`
|
UserLevel uint32 `json:"userLevel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *DokodemoConfig) Build() (proto.Message, error) {
|
func (v *DokodemoConfig) Build() (proto.Message, error) {
|
||||||
config := new(dokodemo.Config)
|
if v.Network != nil {
|
||||||
if v.Address != nil {
|
v.AllowedNetwork = v.Network
|
||||||
config.Address = v.Address.Build()
|
|
||||||
}
|
}
|
||||||
config.Port = uint32(v.Port)
|
if v.Address != nil {
|
||||||
|
v.RewriteAddress = v.Address
|
||||||
|
}
|
||||||
|
if v.Port != 0 {
|
||||||
|
v.RewritePort = v.Port
|
||||||
|
}
|
||||||
|
config := new(dokodemo.Config)
|
||||||
|
config.AllowedNetworks = v.AllowedNetwork.Build()
|
||||||
|
if v.RewriteAddress != nil {
|
||||||
|
config.RewriteAddress = v.RewriteAddress.Build()
|
||||||
|
}
|
||||||
|
config.RewritePort = uint32(v.RewritePort)
|
||||||
config.PortMap = v.PortMap
|
config.PortMap = v.PortMap
|
||||||
for _, v := range config.PortMap {
|
for _, v := range config.PortMap {
|
||||||
if _, _, err := net.SplitHostPort(v); err != nil {
|
if _, _, err := net.SplitHostPort(v); err != nil {
|
||||||
return nil, errors.New("invalid portMap: ", v).Base(err)
|
return nil, errors.New("invalid portMap: ", v).Base(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
config.Networks = v.Network.Build()
|
|
||||||
config.FollowRedirect = v.FollowRedirect
|
config.FollowRedirect = v.FollowRedirect
|
||||||
config.UserLevel = v.UserLevel
|
config.UserLevel = v.UserLevel
|
||||||
return config, nil
|
return config, nil
|
||||||
|
|||||||
@@ -24,15 +24,15 @@ func TestDokodemoConfig(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &dokodemo.Config{
|
Output: &dokodemo.Config{
|
||||||
Address: &net.IPOrDomain{
|
RewriteAddress: &net.IPOrDomain{
|
||||||
Address: &net.IPOrDomain_Ip{
|
Address: &net.IPOrDomain_Ip{
|
||||||
Ip: []byte{8, 8, 8, 8},
|
Ip: []byte{8, 8, 8, 8},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Port: 53,
|
RewritePort: 53,
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
FollowRedirect: true,
|
FollowRedirect: true,
|
||||||
UserLevel: 1,
|
UserLevel: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
+9
-4
@@ -23,6 +23,7 @@ func (v *HTTPAccount) Build() *http.Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HTTPServerConfig struct {
|
type HTTPServerConfig struct {
|
||||||
|
Users []*HTTPAccount `json:"users"`
|
||||||
Accounts []*HTTPAccount `json:"accounts"`
|
Accounts []*HTTPAccount `json:"accounts"`
|
||||||
Transparent bool `json:"allowTransparent"`
|
Transparent bool `json:"allowTransparent"`
|
||||||
UserLevel uint32 `json:"userLevel"`
|
UserLevel uint32 `json:"userLevel"`
|
||||||
@@ -34,9 +35,13 @@ func (c *HTTPServerConfig) Build() (proto.Message, error) {
|
|||||||
UserLevel: c.UserLevel,
|
UserLevel: c.UserLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Accounts) > 0 {
|
if c.Accounts != nil {
|
||||||
|
c.Users = c.Accounts
|
||||||
|
}
|
||||||
|
// TODO: PB
|
||||||
|
if len(c.Users) > 0 {
|
||||||
config.Accounts = make(map[string]string)
|
config.Accounts = make(map[string]string)
|
||||||
for _, account := range c.Accounts {
|
for _, account := range c.Users {
|
||||||
config.Accounts[account.Username] = account.Password
|
config.Accounts[account.Username] = account.Password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,8 +56,8 @@ type HTTPRemoteConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HTTPClientConfig struct {
|
type HTTPClientConfig struct {
|
||||||
Address *Address `json:"address"`
|
Address *Address `json:"address"`
|
||||||
Port uint16 `json:"port"`
|
Port uint16 `json:"port"`
|
||||||
Level uint32 `json:"level"`
|
Level uint32 `json:"level"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Username string `json:"user"`
|
Username string `json:"user"`
|
||||||
|
|||||||
@@ -39,12 +39,16 @@ type HysteriaUserConfig struct {
|
|||||||
|
|
||||||
type HysteriaServerConfig struct {
|
type HysteriaServerConfig struct {
|
||||||
Version int32 `json:"version"`
|
Version int32 `json:"version"`
|
||||||
Users []*HysteriaUserConfig `json:"clients"`
|
Users []*HysteriaUserConfig `json:"users"`
|
||||||
|
Clients []*HysteriaUserConfig `json:"clients"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *HysteriaServerConfig) Build() (proto.Message, error) {
|
func (c *HysteriaServerConfig) Build() (proto.Message, error) {
|
||||||
config := new(hysteria.ServerConfig)
|
config := new(hysteria.ServerConfig)
|
||||||
|
|
||||||
|
if c.Clients != nil {
|
||||||
|
c.Users = c.Clients
|
||||||
|
}
|
||||||
if len(c.Users) > 0 {
|
if len(c.Users) > 0 {
|
||||||
config.Users = make([]*protocol.User, len(c.Users))
|
config.Users = make([]*protocol.User, len(c.Users))
|
||||||
processUser := func(idx int) error {
|
processUser := func(idx int) error {
|
||||||
|
|||||||
@@ -45,13 +45,18 @@ type ShadowsocksServerConfig struct {
|
|||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Level byte `json:"level"`
|
Level byte `json:"level"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Users []*ShadowsocksUserConfig `json:"clients"`
|
Users []*ShadowsocksUserConfig `json:"users"`
|
||||||
|
Clients []*ShadowsocksUserConfig `json:"clients"`
|
||||||
NetworkList *NetworkList `json:"network"`
|
NetworkList *NetworkList `json:"network"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
|
func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
|
||||||
errors.PrintNonRemovalDeprecatedFeatureWarning("Shadowsocks (with no Forward Secrecy, etc.)", "VLESS Encryption")
|
errors.PrintNonRemovalDeprecatedFeatureWarning("Shadowsocks (with no Forward Secrecy, etc.)", "VLESS Encryption")
|
||||||
|
|
||||||
|
if v.Clients != nil {
|
||||||
|
v.Users = v.Clients
|
||||||
|
}
|
||||||
|
|
||||||
if C.Contains(shadowaead_2022.List, v.Cipher) {
|
if C.Contains(shadowaead_2022.List, v.Cipher) {
|
||||||
return buildShadowsocks2022(v)
|
return buildShadowsocks2022(v)
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-3
@@ -29,6 +29,7 @@ const (
|
|||||||
|
|
||||||
type SocksServerConfig struct {
|
type SocksServerConfig struct {
|
||||||
AuthMethod string `json:"auth"`
|
AuthMethod string `json:"auth"`
|
||||||
|
Users []*SocksAccount `json:"users"`
|
||||||
Accounts []*SocksAccount `json:"accounts"`
|
Accounts []*SocksAccount `json:"accounts"`
|
||||||
UDP bool `json:"udp"`
|
UDP bool `json:"udp"`
|
||||||
Host *Address `json:"ip"`
|
Host *Address `json:"ip"`
|
||||||
@@ -47,9 +48,13 @@ func (v *SocksServerConfig) Build() (proto.Message, error) {
|
|||||||
config.AuthType = socks.AuthType_NO_AUTH
|
config.AuthType = socks.AuthType_NO_AUTH
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v.Accounts) > 0 {
|
if v.Accounts != nil {
|
||||||
config.Accounts = make(map[string]string, len(v.Accounts))
|
v.Users = v.Accounts
|
||||||
for _, account := range v.Accounts {
|
}
|
||||||
|
// TODO: PB
|
||||||
|
if len(v.Users) > 0 {
|
||||||
|
config.Accounts = make(map[string]string, len(v.Users))
|
||||||
|
for _, account := range v.Users {
|
||||||
config.Accounts[account.Username] = account.Password
|
config.Accounts[account.Username] = account.Password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ type TrojanUserConfig struct {
|
|||||||
|
|
||||||
// TrojanServerConfig is Inbound configuration
|
// TrojanServerConfig is Inbound configuration
|
||||||
type TrojanServerConfig struct {
|
type TrojanServerConfig struct {
|
||||||
|
Users []*TrojanUserConfig `json:"users"`
|
||||||
Clients []*TrojanUserConfig `json:"clients"`
|
Clients []*TrojanUserConfig `json:"clients"`
|
||||||
Fallbacks []*TrojanInboundFallback `json:"fallbacks"`
|
Fallbacks []*TrojanInboundFallback `json:"fallbacks"`
|
||||||
}
|
}
|
||||||
@@ -120,12 +121,16 @@ type TrojanServerConfig struct {
|
|||||||
func (c *TrojanServerConfig) Build() (proto.Message, error) {
|
func (c *TrojanServerConfig) Build() (proto.Message, error) {
|
||||||
errors.PrintNonRemovalDeprecatedFeatureWarning("Trojan (with no Flow, etc.)", "VLESS with Flow & Seed")
|
errors.PrintNonRemovalDeprecatedFeatureWarning("Trojan (with no Flow, etc.)", "VLESS with Flow & Seed")
|
||||||
|
|
||||||
|
if c.Clients != nil {
|
||||||
|
c.Users = c.Clients
|
||||||
|
}
|
||||||
|
|
||||||
config := &trojan.ServerConfig{
|
config := &trojan.ServerConfig{
|
||||||
Users: make([]*protocol.User, len(c.Clients)),
|
Users: make([]*protocol.User, len(c.Users)),
|
||||||
}
|
}
|
||||||
|
|
||||||
processClient := func(idx int) error {
|
processClient := func(idx int) error {
|
||||||
rawUser := c.Clients[idx]
|
rawUser := c.Users[idx]
|
||||||
if rawUser.Flow != "" {
|
if rawUser.Flow != "" {
|
||||||
return errors.PrintRemovedFeatureError(`Flow for Trojan`, ``)
|
return errors.PrintRemovedFeatureError(`Flow for Trojan`, ``)
|
||||||
}
|
}
|
||||||
@@ -139,7 +144,7 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := task.ParallelForN(len(c.Clients), processClient); err != nil {
|
if err := task.ParallelForN(len(c.Users), processClient); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-10
@@ -31,6 +31,7 @@ type VLessInboundFallback struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VLessInboundConfig struct {
|
type VLessInboundConfig struct {
|
||||||
|
Users []json.RawMessage `json:"users"`
|
||||||
Clients []json.RawMessage `json:"clients"`
|
Clients []json.RawMessage `json:"clients"`
|
||||||
Decryption string `json:"decryption"`
|
Decryption string `json:"decryption"`
|
||||||
Fallbacks []*VLessInboundFallback `json:"fallbacks"`
|
Fallbacks []*VLessInboundFallback `json:"fallbacks"`
|
||||||
@@ -41,21 +42,25 @@ type VLessInboundConfig struct {
|
|||||||
// Build implements Buildable
|
// Build implements Buildable
|
||||||
func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||||
config := new(inbound.Config)
|
config := new(inbound.Config)
|
||||||
config.Clients = make([]*protocol.User, len(c.Clients))
|
|
||||||
|
if c.Clients != nil {
|
||||||
|
c.Users = c.Clients
|
||||||
|
}
|
||||||
|
config.Users = make([]*protocol.User, len(c.Users))
|
||||||
switch c.Flow {
|
switch c.Flow {
|
||||||
case vless.XRV, "":
|
case vless.XRV, "":
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(`VLESS "settings.flow" doesn't support "` + c.Flow + `" in this version`)
|
return nil, errors.New(`VLESS "settings.flow" doesn't support "` + c.Flow + `" in this version`)
|
||||||
}
|
}
|
||||||
processClient := func(idx int) error {
|
processClient := func(idx int) error {
|
||||||
rawUser := c.Clients[idx]
|
rawUser := c.Users[idx]
|
||||||
user := new(protocol.User)
|
user := new(protocol.User)
|
||||||
if err := json.Unmarshal(rawUser, user); err != nil {
|
if err := json.Unmarshal(rawUser, user); err != nil {
|
||||||
return errors.New(`VLESS clients: invalid user`).Base(err)
|
return errors.New(`VLESS users: invalid user`).Base(err)
|
||||||
}
|
}
|
||||||
account := new(vless.Account)
|
account := new(vless.Account)
|
||||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||||
return errors.New(`VLESS clients: invalid user`).Base(err)
|
return errors.New(`VLESS users: invalid user`).Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := uuid.ParseString(account.Id)
|
u, err := uuid.ParseString(account.Id)
|
||||||
@@ -69,7 +74,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
|||||||
account.Flow = c.Flow
|
account.Flow = c.Flow
|
||||||
case vless.XRV:
|
case vless.XRV:
|
||||||
default:
|
default:
|
||||||
return errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
|
return errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(account.Testseed) < 4 {
|
if len(account.Testseed) < 4 {
|
||||||
@@ -77,24 +82,24 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if account.Encryption != "" {
|
if account.Encryption != "" {
|
||||||
return errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
|
return errors.New(`VLESS users: "encryption" should not be in inbound settings`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.Reverse != nil {
|
if account.Reverse != nil {
|
||||||
if account.Reverse.Tag == "" {
|
if account.Reverse.Tag == "" {
|
||||||
return errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
|
return errors.New(`VLESS users: "tag" can't be empty for "reverse"`)
|
||||||
}
|
}
|
||||||
if account.Reverse.Sniffing != nil { // may not be reached: error json unmarshal
|
if account.Reverse.Sniffing != nil { // may not be reached: error json unmarshal
|
||||||
return errors.New(`VLESS clients: inbound's "reverse" can't have "sniffing"`)
|
return errors.New(`VLESS users: inbound's "reverse" can't have "sniffing"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Account = serial.ToTypedMessage(account)
|
user.Account = serial.ToTypedMessage(account)
|
||||||
config.Clients[idx] = user
|
config.Users[idx] = user
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := task.ParallelForN(len(c.Clients), processClient); err != nil {
|
if err := task.ParallelForN(len(c.Users), processClient); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func TestVLessInbound(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
Parser: loadJSON(creator),
|
Parser: loadJSON(creator),
|
||||||
Output: &inbound.Config{
|
Output: &inbound.Config{
|
||||||
Clients: []*protocol.User{
|
Users: []*protocol.User{
|
||||||
{
|
{
|
||||||
Account: serial.ToTypedMessage(&vless.Account{
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
Id: "27848739-7e62-4138-9fd3-098a63964b6b",
|
Id: "27848739-7e62-4138-9fd3-098a63964b6b",
|
||||||
|
|||||||
+5
-1
@@ -59,7 +59,8 @@ func (c *VMessDefaultConfig) Build() *inbound.DefaultConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VMessInboundConfig struct {
|
type VMessInboundConfig struct {
|
||||||
Users []json.RawMessage `json:"clients"`
|
Users []json.RawMessage `json:"users"`
|
||||||
|
Clients []json.RawMessage `json:"clients"`
|
||||||
Defaults *VMessDefaultConfig `json:"default"`
|
Defaults *VMessDefaultConfig `json:"default"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +74,9 @@ func (c *VMessInboundConfig) Build() (proto.Message, error) {
|
|||||||
config.Default = c.Defaults.Build()
|
config.Default = c.Defaults.Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Clients != nil {
|
||||||
|
c.Users = c.Clients
|
||||||
|
}
|
||||||
config.User = make([]*protocol.User, len(c.Users))
|
config.User = make([]*protocol.User, len(c.Users))
|
||||||
processUser := func(idx int) error {
|
processUser := func(idx int) error {
|
||||||
rawData := c.Users[idx]
|
rawData := c.Users[idx]
|
||||||
|
|||||||
@@ -173,6 +173,26 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
receiverSettings.StreamSettings = ss
|
receiverSettings.StreamSettings = ss
|
||||||
|
// TODO: Actually implement this breaking change
|
||||||
|
protocol := ss.GetEffectiveProtocol()
|
||||||
|
if (protocol == "websocket" || protocol == "httpupgrade" || protocol == "splithttp") &&
|
||||||
|
(c.StreamSetting.SocketSettings == nil || len(c.StreamSetting.SocketSettings.TrustedXForwardedFor) == 0) {
|
||||||
|
errors.LogWarning(context.Background(),
|
||||||
|
`====== SECURITY WARNING ======`,
|
||||||
|
"\n",
|
||||||
|
`inbound "`, c.Tag, `" using `, protocol, ` has not configured "sockopt.trustedXForwardedFor".`,
|
||||||
|
"\n",
|
||||||
|
`THIS IS VERY INSECURE!!!`,
|
||||||
|
"\n",
|
||||||
|
`For compatibility, Xray still allows this for now and still trusts X-Forwarded-For implicitly.`,
|
||||||
|
"\n",
|
||||||
|
`Please configure "sockopt.trustedXForwardedFor" immediately.`,
|
||||||
|
"\n",
|
||||||
|
`In future versions, this option must be explicitly set.`,
|
||||||
|
"\n",
|
||||||
|
`====== SECURITY WARNING ======`,
|
||||||
|
)
|
||||||
|
}
|
||||||
if strings.Contains(ss.SecurityType, "reality") && (receiverSettings.PortList == nil ||
|
if strings.Contains(ss.SecurityType, "reality") && (receiverSettings.PortList == nil ||
|
||||||
len(receiverSettings.PortList.Ports()) != 1 || receiverSettings.PortList.Ports()[0] != 443) {
|
len(receiverSettings.PortList.Ports()) != 1 || receiverSettings.PortList.Ports()[0] != 443) {
|
||||||
errors.LogWarning(context.Background(), `REALITY: Listening on non-443 ports may get your IP blocked by the GFW`)
|
errors.LogWarning(context.Background(), `REALITY: Listening on non-443 ports may get your IP blocked by the GFW`)
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func extractInboundUsers(inb *core.InboundHandlerConfig) []*protocol.User {
|
|||||||
case *vmessin.Config:
|
case *vmessin.Config:
|
||||||
return ty.User
|
return ty.User
|
||||||
case *vlessin.Config:
|
case *vlessin.Config:
|
||||||
return ty.Clients
|
return ty.Users
|
||||||
case *trojan.ServerConfig:
|
case *trojan.ServerConfig:
|
||||||
return ty.Users
|
return ty.Users
|
||||||
case *shadowsocks.ServerConfig:
|
case *shadowsocks.ServerConfig:
|
||||||
|
|||||||
Vendored
+61
-52
@@ -3,8 +3,8 @@ package external
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"net"
|
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/utils"
|
||||||
"github.com/xtls/xray-core/main/confloader"
|
"github.com/xtls/xray-core/main/confloader"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,9 +21,10 @@ func ConfigLoader(arg string) (out io.Reader, err error) {
|
|||||||
var data []byte
|
var data []byte
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(arg, "http+unix://"):
|
case strings.HasPrefix(arg, "http+unix://"):
|
||||||
data, err = FetchUnixSocketHTTPContent(arg)
|
errors.PrintDeprecatedFeatureWarning(`"http+unix://" prefix`, `direct Unix socket path (e.g. /path/socket.sock:/api or @abstract:/api)`)
|
||||||
|
data, err = FetchHTTPContent(httpUnixToCanonical(arg))
|
||||||
|
|
||||||
case strings.HasPrefix(arg, "http://"), strings.HasPrefix(arg, "https://"):
|
case isRemoteSource(arg):
|
||||||
data, err = FetchHTTPContent(arg)
|
data, err = FetchHTTPContent(arg)
|
||||||
|
|
||||||
case arg == "stdin:":
|
case arg == "stdin:":
|
||||||
@@ -39,19 +41,38 @@ func ConfigLoader(arg string) (out io.Reader, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FetchHTTPContent issues an HTTP GET against either a regular HTTP(S) URL
|
||||||
|
// or a Unix socket HTTP endpoint.
|
||||||
|
//
|
||||||
|
// http(s)://host/api regular HTTP(S)
|
||||||
|
// /path/to/socket.sock[:/api] filesystem socket
|
||||||
|
// @abstract[:/api] abstract socket (Linux/Android)
|
||||||
|
// @@padded[:/api] padded abstract socket (HAProxy compat)
|
||||||
|
//
|
||||||
|
// When the ":/" separator is omitted on a socket target, the request is
|
||||||
|
// made to "/".
|
||||||
func FetchHTTPContent(target string) ([]byte, error) {
|
func FetchHTTPContent(target string) ([]byte, error) {
|
||||||
parsedTarget, err := url.Parse(target)
|
httpURL, socketPath := utils.SplitHTTPUnixURL(target)
|
||||||
|
|
||||||
|
parsedTarget, err := url.Parse(httpURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("invalid URL: ", target).Base(err)
|
return nil, errors.New("invalid URL: ", target).Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" {
|
|
||||||
return nil, errors.New("invalid scheme: ", parsedTarget.Scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if socketPath != "" {
|
||||||
|
dialAddr := utils.ResolveSocketPath(socketPath)
|
||||||
|
client.Transport = &http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
var d net.Dialer
|
||||||
|
return d.DialContext(ctx, "unix", dialAddr)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := client.Do(&http.Request{
|
resp, err := client.Do(&http.Request{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
URL: parsedTarget,
|
URL: parsedTarget,
|
||||||
@@ -74,58 +95,46 @@ func FetchHTTPContent(target string) ([]byte, error) {
|
|||||||
return content, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format: http+unix:///path/to/socket.sock/api/endpoint
|
|
||||||
func FetchUnixSocketHTTPContent(target string) ([]byte, error) {
|
|
||||||
path := strings.TrimPrefix(target, "http+unix://")
|
|
||||||
|
|
||||||
if !strings.HasPrefix(path, "/") {
|
// isRemoteSource reports whether arg should be fetched via HTTP (regular
|
||||||
return nil, errors.New("unix socket path must be absolute")
|
// network or Unix socket) rather than read from the local filesystem.
|
||||||
|
// Recognized forms:
|
||||||
|
//
|
||||||
|
// - http(s)://... regular HTTP(S)
|
||||||
|
// - @abstract[:/api] abstract socket (Linux/Android)
|
||||||
|
// - /abs/path:/api filesystem socket, explicit HTTP path
|
||||||
|
// - /abs/path filesystem socket detected via os.ModeSocket
|
||||||
|
func isRemoteSource(arg string) bool {
|
||||||
|
if arg == "" {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
|
||||||
var socketPath, httpPath string
|
return true
|
||||||
|
|
||||||
sockIdx := strings.Index(path, ".sock")
|
|
||||||
if sockIdx != -1 {
|
|
||||||
socketPath = path[:sockIdx+5]
|
|
||||||
httpPath = path[sockIdx+5:]
|
|
||||||
if httpPath == "" {
|
|
||||||
httpPath = "/"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("cannot determine socket path, socket file should have .sock extension")
|
|
||||||
}
|
}
|
||||||
|
if arg[0] == '@' {
|
||||||
if _, err := os.Stat(socketPath); err != nil {
|
return true
|
||||||
return nil, errors.New("socket file not found: ", socketPath).Base(err)
|
|
||||||
}
|
}
|
||||||
|
if arg[0] != '/' {
|
||||||
client := &http.Client{
|
return false
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
Transport: &http.Transport{
|
|
||||||
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
|
||||||
var d net.Dialer
|
|
||||||
return d.DialContext(ctx, "unix", socketPath)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
defer client.CloseIdleConnections()
|
if strings.Contains(arg, ":/") {
|
||||||
|
return true
|
||||||
resp, err := client.Get("http://localhost" + httpPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("failed to fetch from unix socket: ", socketPath).Base(err)
|
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
info, err := os.Stat(arg)
|
||||||
|
return err == nil && info.Mode()&os.ModeSocket != 0
|
||||||
|
}
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return nil, errors.New("unexpected HTTP status code: ", resp.StatusCode)
|
// httpUnixToCanonical converts the deprecated http+unix:///path/to/socket.sock/api
|
||||||
|
// URL into the canonical /path/to/socket.sock:/api form by inserting ":"
|
||||||
|
// between the ".sock" extension and the HTTP path. Inputs without a path
|
||||||
|
// after ".sock" are returned with just the "http+unix://" prefix stripped.
|
||||||
|
func httpUnixToCanonical(target string) string {
|
||||||
|
raw := strings.TrimPrefix(target, "http+unix://")
|
||||||
|
if i := strings.Index(raw, ".sock/"); i >= 0 {
|
||||||
|
raw = raw[:i+5] + ":" + raw[i+5:]
|
||||||
}
|
}
|
||||||
|
return raw
|
||||||
content, err := buf.ReadAllToBytes(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("failed to read response").Base(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return content, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ type Config struct {
|
|||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
UserLevel uint32 `protobuf:"varint,1,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
UserLevel uint32 `protobuf:"varint,1,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
||||||
Rule []*DNSRuleConfig `protobuf:"bytes,2,rep,name=rule,proto3" json:"rule,omitempty"`
|
Rule []*DNSRuleConfig `protobuf:"bytes,2,rep,name=rule,proto3" json:"rule,omitempty"`
|
||||||
Server *net.Endpoint `protobuf:"bytes,3,opt,name=server,proto3" json:"server,omitempty"`
|
RewriteServer *net.Endpoint `protobuf:"bytes,3,opt,name=rewrite_server,json=rewriteServer,proto3" json:"rewrite_server,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -188,9 +188,9 @@ func (x *Config) GetRule() []*DNSRuleConfig {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetServer() *net.Endpoint {
|
func (x *Config) GetRewriteServer() *net.Endpoint {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Server
|
return x.RewriteServer
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -203,12 +203,12 @@ const file_proxy_dns_config_proto_rawDesc = "" +
|
|||||||
"\rDNSRuleConfig\x122\n" +
|
"\rDNSRuleConfig\x122\n" +
|
||||||
"\x06action\x18\x01 \x01(\x0e2\x1a.xray.proxy.dns.RuleActionR\x06action\x12\x14\n" +
|
"\x06action\x18\x01 \x01(\x0e2\x1a.xray.proxy.dns.RuleActionR\x06action\x12\x14\n" +
|
||||||
"\x05qtype\x18\x02 \x03(\x05R\x05qtype\x127\n" +
|
"\x05qtype\x18\x02 \x03(\x05R\x05qtype\x127\n" +
|
||||||
"\x06domain\x18\x03 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x06domain\"\x8d\x01\n" +
|
"\x06domain\x18\x03 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x06domain\"\x9c\x01\n" +
|
||||||
"\x06Config\x12\x1d\n" +
|
"\x06Config\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"user_level\x18\x01 \x01(\rR\tuserLevel\x121\n" +
|
"user_level\x18\x01 \x01(\rR\tuserLevel\x121\n" +
|
||||||
"\x04rule\x18\x02 \x03(\v2\x1d.xray.proxy.dns.DNSRuleConfigR\x04rule\x121\n" +
|
"\x04rule\x18\x02 \x03(\v2\x1d.xray.proxy.dns.DNSRuleConfigR\x04rule\x12@\n" +
|
||||||
"\x06server\x18\x03 \x01(\v2\x19.xray.common.net.EndpointR\x06server*:\n" +
|
"\x0erewrite_server\x18\x03 \x01(\v2\x19.xray.common.net.EndpointR\rrewriteServer*:\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"RuleAction\x12\n" +
|
"RuleAction\x12\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
@@ -245,7 +245,7 @@ var file_proxy_dns_config_proto_depIdxs = []int32{
|
|||||||
0, // 0: xray.proxy.dns.DNSRuleConfig.action:type_name -> xray.proxy.dns.RuleAction
|
0, // 0: xray.proxy.dns.DNSRuleConfig.action:type_name -> xray.proxy.dns.RuleAction
|
||||||
3, // 1: xray.proxy.dns.DNSRuleConfig.domain:type_name -> xray.common.geodata.DomainRule
|
3, // 1: xray.proxy.dns.DNSRuleConfig.domain:type_name -> xray.common.geodata.DomainRule
|
||||||
1, // 2: xray.proxy.dns.Config.rule:type_name -> xray.proxy.dns.DNSRuleConfig
|
1, // 2: xray.proxy.dns.Config.rule:type_name -> xray.proxy.dns.DNSRuleConfig
|
||||||
4, // 3: xray.proxy.dns.Config.server:type_name -> xray.common.net.Endpoint
|
4, // 3: xray.proxy.dns.Config.rewrite_server:type_name -> xray.common.net.Endpoint
|
||||||
4, // [4:4] is the sub-list for method output_type
|
4, // [4:4] is the sub-list for method output_type
|
||||||
4, // [4:4] is the sub-list for method input_type
|
4, // [4:4] is the sub-list for method input_type
|
||||||
4, // [4:4] is the sub-list for extension type_name
|
4, // [4:4] is the sub-list for extension type_name
|
||||||
|
|||||||
@@ -25,5 +25,5 @@ message DNSRuleConfig {
|
|||||||
message Config {
|
message Config {
|
||||||
uint32 user_level = 1;
|
uint32 user_level = 1;
|
||||||
repeated DNSRuleConfig rule = 2;
|
repeated DNSRuleConfig rule = 2;
|
||||||
xray.common.net.Endpoint server = 3;
|
xray.common.net.Endpoint rewrite_server = 3;
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -74,7 +74,7 @@ type Handler struct {
|
|||||||
client dns.Client
|
client dns.Client
|
||||||
fdns dns.FakeDNSEngine
|
fdns dns.FakeDNSEngine
|
||||||
ownLinkVerifier ownLinkVerifier
|
ownLinkVerifier ownLinkVerifier
|
||||||
server net.Destination
|
rewriteServer net.Destination
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
rules []*DNSRule
|
rules []*DNSRule
|
||||||
}
|
}
|
||||||
@@ -87,8 +87,8 @@ func (h *Handler) Init(config *Config, dnsClient dns.Client, policyManager polic
|
|||||||
h.ownLinkVerifier = v
|
h.ownLinkVerifier = v
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Server != nil {
|
if config.RewriteServer != nil {
|
||||||
h.server = config.Server.AsDestination()
|
h.rewriteServer = config.RewriteServer.AsDestination()
|
||||||
}
|
}
|
||||||
|
|
||||||
h.rules = make([]*DNSRule, 0, len(config.Rule))
|
h.rules = make([]*DNSRule, 0, len(config.Rule))
|
||||||
@@ -161,14 +161,14 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
|
|||||||
srcNetwork := ob.Target.Network
|
srcNetwork := ob.Target.Network
|
||||||
|
|
||||||
dest := ob.Target
|
dest := ob.Target
|
||||||
if h.server.Network != net.Network_Unknown {
|
if h.rewriteServer.Network != net.Network_Unknown {
|
||||||
dest.Network = h.server.Network
|
dest.Network = h.rewriteServer.Network
|
||||||
}
|
}
|
||||||
if h.server.Address != nil {
|
if h.rewriteServer.Address != nil {
|
||||||
dest.Address = h.server.Address
|
dest.Address = h.rewriteServer.Address
|
||||||
}
|
}
|
||||||
if h.server.Port != 0 {
|
if h.rewriteServer.Port != 0 {
|
||||||
dest.Port = h.server.Port
|
dest.Port = h.rewriteServer.Port
|
||||||
}
|
}
|
||||||
|
|
||||||
errors.LogInfo(ctx, "handling DNS traffic to ", dest)
|
errors.LogInfo(ctx, "handling DNS traffic to ", dest)
|
||||||
|
|||||||
+13
-13
@@ -114,9 +114,9 @@ func TestUDPDNSTunnel(t *testing.T) {
|
|||||||
Inbound: []*core.InboundHandlerConfig{
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
{
|
{
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
RewriteAddress: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(port),
|
RewritePort: uint32(port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
||||||
@@ -233,9 +233,9 @@ func TestTCPDNSTunnel(t *testing.T) {
|
|||||||
Inbound: []*core.InboundHandlerConfig{
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
{
|
{
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
RewriteAddress: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(port),
|
RewritePort: uint32(port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
||||||
@@ -319,9 +319,9 @@ func TestUDP2TCPDNSTunnel(t *testing.T) {
|
|||||||
Inbound: []*core.InboundHandlerConfig{
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
{
|
{
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
RewriteAddress: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(port),
|
RewritePort: uint32(port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
||||||
@@ -332,7 +332,7 @@ func TestUDP2TCPDNSTunnel(t *testing.T) {
|
|||||||
Outbound: []*core.OutboundHandlerConfig{
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
{
|
{
|
||||||
ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{
|
ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{
|
||||||
Server: &net.Endpoint{
|
RewriteServer: &net.Endpoint{
|
||||||
Network: net.Network_TCP,
|
Network: net.Network_TCP,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -409,9 +409,9 @@ func TestDNSRules(t *testing.T) {
|
|||||||
Inbound: []*core.InboundHandlerConfig{
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
{
|
{
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
RewriteAddress: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(port),
|
RewritePort: uint32(port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
// GetPredefinedAddress returns the defined address from proto config. Null if address is not valid.
|
// GetPredefinedAddress returns the defined address from proto config. Null if address is not valid.
|
||||||
func (v *Config) GetPredefinedAddress() net.Address {
|
func (v *Config) GetPredefinedAddress() net.Address {
|
||||||
addr := v.Address.AsAddress()
|
addr := v.RewriteAddress.AsAddress()
|
||||||
if addr == nil {
|
if addr == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-23
@@ -23,16 +23,16 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
RewriteAddress *net.IPOrDomain `protobuf:"bytes,1,opt,name=rewrite_address,json=rewriteAddress,proto3" json:"rewrite_address,omitempty"`
|
||||||
Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
|
RewritePort uint32 `protobuf:"varint,2,opt,name=rewrite_port,json=rewritePort,proto3" json:"rewrite_port,omitempty"`
|
||||||
PortMap map[string]string `protobuf:"bytes,3,rep,name=port_map,json=portMap,proto3" json:"port_map,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
PortMap map[string]string `protobuf:"bytes,3,rep,name=port_map,json=portMap,proto3" json:"port_map,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||||
// List of networks that the Dokodemo accepts.
|
// List of networks that the Dokodemo accepts.
|
||||||
Networks []net.Network `protobuf:"varint,7,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"`
|
AllowedNetworks []net.Network `protobuf:"varint,7,rep,packed,name=allowed_networks,json=allowedNetworks,proto3,enum=xray.common.net.Network" json:"allowed_networks,omitempty"`
|
||||||
FollowRedirect bool `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"`
|
FollowRedirect bool `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"`
|
||||||
UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -65,16 +65,16 @@ func (*Config) Descriptor() ([]byte, []int) {
|
|||||||
return file_proxy_dokodemo_config_proto_rawDescGZIP(), []int{0}
|
return file_proxy_dokodemo_config_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetAddress() *net.IPOrDomain {
|
func (x *Config) GetRewriteAddress() *net.IPOrDomain {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Address
|
return x.RewriteAddress
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetPort() uint32 {
|
func (x *Config) GetRewritePort() uint32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Port
|
return x.RewritePort
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -86,9 +86,9 @@ func (x *Config) GetPortMap() map[string]string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetNetworks() []net.Network {
|
func (x *Config) GetAllowedNetworks() []net.Network {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Networks
|
return x.AllowedNetworks
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -111,12 +111,12 @@ var File_proxy_dokodemo_config_proto protoreflect.FileDescriptor
|
|||||||
|
|
||||||
const file_proxy_dokodemo_config_proto_rawDesc = "" +
|
const file_proxy_dokodemo_config_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x1bproxy/dokodemo/config.proto\x12\x13xray.proxy.dokodemo\x1a\x18common/net/address.proto\x1a\x18common/net/network.proto\"\xd2\x02\n" +
|
"\x1bproxy/dokodemo/config.proto\x12\x13xray.proxy.dokodemo\x1a\x18common/net/address.proto\x1a\x18common/net/network.proto\"\xff\x02\n" +
|
||||||
"\x06Config\x125\n" +
|
"\x06Config\x12D\n" +
|
||||||
"\aaddress\x18\x01 \x01(\v2\x1b.xray.common.net.IPOrDomainR\aaddress\x12\x12\n" +
|
"\x0frewrite_address\x18\x01 \x01(\v2\x1b.xray.common.net.IPOrDomainR\x0erewriteAddress\x12!\n" +
|
||||||
"\x04port\x18\x02 \x01(\rR\x04port\x12C\n" +
|
"\frewrite_port\x18\x02 \x01(\rR\vrewritePort\x12C\n" +
|
||||||
"\bport_map\x18\x03 \x03(\v2(.xray.proxy.dokodemo.Config.PortMapEntryR\aportMap\x124\n" +
|
"\bport_map\x18\x03 \x03(\v2(.xray.proxy.dokodemo.Config.PortMapEntryR\aportMap\x12C\n" +
|
||||||
"\bnetworks\x18\a \x03(\x0e2\x18.xray.common.net.NetworkR\bnetworks\x12'\n" +
|
"\x10allowed_networks\x18\a \x03(\x0e2\x18.xray.common.net.NetworkR\x0fallowedNetworks\x12'\n" +
|
||||||
"\x0ffollow_redirect\x18\x05 \x01(\bR\x0efollowRedirect\x12\x1d\n" +
|
"\x0ffollow_redirect\x18\x05 \x01(\bR\x0efollowRedirect\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"user_level\x18\x06 \x01(\rR\tuserLevel\x1a:\n" +
|
"user_level\x18\x06 \x01(\rR\tuserLevel\x1a:\n" +
|
||||||
@@ -145,9 +145,9 @@ var file_proxy_dokodemo_config_proto_goTypes = []any{
|
|||||||
(net.Network)(0), // 3: xray.common.net.Network
|
(net.Network)(0), // 3: xray.common.net.Network
|
||||||
}
|
}
|
||||||
var file_proxy_dokodemo_config_proto_depIdxs = []int32{
|
var file_proxy_dokodemo_config_proto_depIdxs = []int32{
|
||||||
2, // 0: xray.proxy.dokodemo.Config.address:type_name -> xray.common.net.IPOrDomain
|
2, // 0: xray.proxy.dokodemo.Config.rewrite_address:type_name -> xray.common.net.IPOrDomain
|
||||||
1, // 1: xray.proxy.dokodemo.Config.port_map:type_name -> xray.proxy.dokodemo.Config.PortMapEntry
|
1, // 1: xray.proxy.dokodemo.Config.port_map:type_name -> xray.proxy.dokodemo.Config.PortMapEntry
|
||||||
3, // 2: xray.proxy.dokodemo.Config.networks:type_name -> xray.common.net.Network
|
3, // 2: xray.proxy.dokodemo.Config.allowed_networks:type_name -> xray.common.net.Network
|
||||||
3, // [3:3] is the sub-list for method output_type
|
3, // [3:3] is the sub-list for method output_type
|
||||||
3, // [3:3] is the sub-list for method input_type
|
3, // [3:3] is the sub-list for method input_type
|
||||||
3, // [3:3] is the sub-list for extension type_name
|
3, // [3:3] is the sub-list for extension type_name
|
||||||
|
|||||||
@@ -10,14 +10,11 @@ import "common/net/address.proto";
|
|||||||
import "common/net/network.proto";
|
import "common/net/network.proto";
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
xray.common.net.IPOrDomain address = 1;
|
|
||||||
uint32 port = 2;
|
|
||||||
|
|
||||||
map<string, string> port_map = 3;
|
|
||||||
|
|
||||||
// List of networks that the Dokodemo accepts.
|
// List of networks that the Dokodemo accepts.
|
||||||
repeated xray.common.net.Network networks = 7;
|
repeated xray.common.net.Network allowed_networks = 7;
|
||||||
|
xray.common.net.IPOrDomain rewrite_address = 1;
|
||||||
|
uint32 rewrite_port = 2;
|
||||||
|
map<string, string> port_map = 3;
|
||||||
bool follow_redirect = 5;
|
bool follow_redirect = 5;
|
||||||
uint32 user_level = 6;
|
uint32 user_level = 6;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-14
@@ -32,22 +32,22 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DokodemoDoor struct {
|
type DokodemoDoor struct {
|
||||||
policyManager policy.Manager
|
policyManager policy.Manager
|
||||||
config *Config
|
config *Config
|
||||||
address net.Address
|
rewriteAddress net.Address
|
||||||
port net.Port
|
rewritePort net.Port
|
||||||
portMap map[string]string
|
portMap map[string]string
|
||||||
sockopt *session.Sockopt
|
sockopt *session.Sockopt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the DokodemoDoor instance with necessary parameters.
|
// Init initializes the DokodemoDoor instance with necessary parameters.
|
||||||
func (d *DokodemoDoor) Init(config *Config, pm policy.Manager, sockopt *session.Sockopt) error {
|
func (d *DokodemoDoor) Init(config *Config, pm policy.Manager, sockopt *session.Sockopt) error {
|
||||||
if len(config.Networks) == 0 {
|
if len(config.AllowedNetworks) == 0 {
|
||||||
return errors.New("no network specified")
|
return errors.New("no network specified")
|
||||||
}
|
}
|
||||||
d.config = config
|
d.config = config
|
||||||
d.address = config.GetPredefinedAddress()
|
d.rewriteAddress = config.GetPredefinedAddress()
|
||||||
d.port = net.Port(config.Port)
|
d.rewritePort = net.Port(config.RewritePort)
|
||||||
d.portMap = config.PortMap
|
d.portMap = config.PortMap
|
||||||
d.policyManager = pm
|
d.policyManager = pm
|
||||||
d.sockopt = sockopt
|
d.sockopt = sockopt
|
||||||
@@ -57,10 +57,10 @@ func (d *DokodemoDoor) Init(config *Config, pm policy.Manager, sockopt *session.
|
|||||||
|
|
||||||
// Network implements proxy.Inbound.
|
// Network implements proxy.Inbound.
|
||||||
func (d *DokodemoDoor) Network() []net.Network {
|
func (d *DokodemoDoor) Network() []net.Network {
|
||||||
if slices.Contains(d.config.Networks, net.Network_TCP) {
|
if slices.Contains(d.config.AllowedNetworks, net.Network_TCP) {
|
||||||
return append(d.config.Networks, net.Network_UNIX)
|
return append(d.config.AllowedNetworks, net.Network_UNIX)
|
||||||
}
|
}
|
||||||
return d.config.Networks
|
return d.config.AllowedNetworks
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DokodemoDoor) policy() policy.Session {
|
func (d *DokodemoDoor) policy() policy.Session {
|
||||||
@@ -78,8 +78,8 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
|
|||||||
}
|
}
|
||||||
dest := net.Destination{
|
dest := net.Destination{
|
||||||
Network: network,
|
Network: network,
|
||||||
Address: d.address,
|
Address: d.rewriteAddress,
|
||||||
Port: d.port,
|
Port: d.rewritePort,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !d.config.FollowRedirect {
|
if !d.config.FollowRedirect {
|
||||||
|
|||||||
+27
-16
@@ -239,11 +239,11 @@ func (h *Handler) blockDelay(rule *FinalRule) time.Duration {
|
|||||||
min = rule.blockDelay.Min
|
min = rule.blockDelay.Min
|
||||||
max = rule.blockDelay.Max
|
max = rule.blockDelay.Max
|
||||||
}
|
}
|
||||||
abs := max - min
|
span := max - min
|
||||||
if max < min {
|
if max < min {
|
||||||
abs = min - max
|
span = min - max
|
||||||
}
|
}
|
||||||
return time.Duration(min+uint64(dice.Roll(int(abs+1)))) * time.Second
|
return time.Duration(min+uint64(dice.Roll(int(span+1)))) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValidAddress(addr *net.IPOrDomain) bool {
|
func isValidAddress(addr *net.IPOrDomain) bool {
|
||||||
@@ -293,6 +293,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
var conn stat.Connection
|
var conn stat.Connection
|
||||||
var blockedDest *net.Destination
|
var blockedDest *net.Destination
|
||||||
var blockedRule *FinalRule
|
var blockedRule *FinalRule
|
||||||
|
firstResolve := true
|
||||||
err := retry.ExponentialBackoff(5, 100).On(func() error {
|
err := retry.ExponentialBackoff(5, 100).On(func() error {
|
||||||
dialDest := destination
|
dialDest := destination
|
||||||
if h.config.DomainStrategy.HasStrategy() && dialDest.Address.Family().IsDomain() {
|
if h.config.DomainStrategy.HasStrategy() && dialDest.Address.Family().IsDomain() {
|
||||||
@@ -303,7 +304,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
ips, err := internet.LookupForIP(dialDest.Address.Domain(), strategy, outGateway)
|
ips, err := internet.LookupForIP(dialDest.Address.Domain(), strategy, outGateway)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
|
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
|
||||||
if h.config.DomainStrategy.ForceIP() {
|
if h.config.DomainStrategy.ForceIP() || h.shouldResolveDomainBeforeFinalRules(dialDest, defaultRule) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -315,14 +316,29 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
errors.LogInfo(ctx, "dialing to ", dialDest)
|
errors.LogInfo(ctx, "dialing to ", dialDest)
|
||||||
}
|
}
|
||||||
} else if h.shouldResolveDomainBeforeFinalRules(dialDest, defaultRule) { // asis + domain + hasrules
|
} else if h.shouldResolveDomainBeforeFinalRules(dialDest, defaultRule) { // asis + domain + hasrules
|
||||||
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, dialDest.Address.Domain())
|
domain := dialDest.Address.Domain()
|
||||||
if err != nil {
|
var ips []net.IP
|
||||||
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
|
if firstResolve {
|
||||||
} else if len(addrs) > 0 {
|
firstResolve = false
|
||||||
if addr := net.IPAddress(addrs[dice.Roll(len(addrs))].IP); addr != nil {
|
supportIPv4, supportIPv6 := utils.CheckRoutes()
|
||||||
dialDest.Address = addr
|
if supportIPv4 {
|
||||||
errors.LogInfo(ctx, "dialing to ", dialDest)
|
ips, _ = net.DefaultResolver.LookupIP(ctx, "ip4", domain)
|
||||||
}
|
}
|
||||||
|
if len(ips) == 0 && supportIPv6 {
|
||||||
|
ips, _ = net.DefaultResolver.LookupIP(ctx, "ip6", domain)
|
||||||
|
}
|
||||||
|
if len(ips) == 0 {
|
||||||
|
return errors.New("failed to get IP address for domain ", domain)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ips, _ = net.DefaultResolver.LookupIP(ctx, "ip", domain)
|
||||||
|
}
|
||||||
|
if len(ips) == 0 { // SRV/TXT, lookup failed
|
||||||
|
return errors.New("failed to get IP address for domain ", domain)
|
||||||
|
}
|
||||||
|
if addr := net.IPAddress(ips[dice.Roll(len(ips))]); addr != nil {
|
||||||
|
dialDest.Address = addr
|
||||||
|
errors.LogInfo(ctx, "dialing to ", dialDest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rule := h.matchFinalRule(dialDest.Network, dialDest.Address, dialDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
if rule := h.matchFinalRule(dialDest.Network, dialDest.Address, dialDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
||||||
@@ -357,11 +373,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// TODO: SRV/TXT
|
|
||||||
// if remoteDest := net.DestinationFromAddr(conn.RemoteAddr()); h.applyFinalRules(remoteDest.Network, remoteDest.Address, remoteDest.Port, defaultRule) == RuleAction_Block {
|
|
||||||
// conn.Close()
|
|
||||||
// return blackhole(remoteDest)
|
|
||||||
// }
|
|
||||||
if h.config.ProxyProtocol > 0 && h.config.ProxyProtocol <= 2 {
|
if h.config.ProxyProtocol > 0 && h.config.ProxyProtocol <= 2 {
|
||||||
version := byte(h.config.ProxyProtocol)
|
version := byte(h.config.ProxyProtocol)
|
||||||
srcAddr := inbound.Source.RawNetAddr()
|
srcAddr := inbound.Source.RawNetAddr()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package shadowsocks_2022
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
shadowsocks "github.com/sagernet/sing-shadowsocks"
|
shadowsocks "github.com/sagernet/sing-shadowsocks"
|
||||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||||
@@ -18,6 +19,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/signal"
|
||||||
"github.com/xtls/xray-core/common/singbridge"
|
"github.com/xtls/xray-core/common/singbridge"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
@@ -115,7 +117,11 @@ func (i *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata M.M
|
|||||||
})
|
})
|
||||||
errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination)
|
errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination)
|
||||||
dispatcher := session.DispatcherFromContext(ctx)
|
dispatcher := session.DispatcherFromContext(ctx)
|
||||||
link, err := dispatcher.Dispatch(ctx, singbridge.ToDestination(metadata.Destination, net.Network_TCP))
|
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_TCP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
link, err := dispatcher.Dispatch(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -136,7 +142,10 @@ func (i *Inbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
})
|
})
|
||||||
errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination)
|
errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination)
|
||||||
dispatcher := session.DispatcherFromContext(ctx)
|
dispatcher := session.DispatcherFromContext(ctx)
|
||||||
destination := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
|
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
link, err := dispatcher.Dispatch(ctx, destination)
|
link, err := dispatcher.Dispatch(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -145,6 +154,9 @@ func (i *Inbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
Reader: link.Reader,
|
Reader: link.Reader,
|
||||||
Writer: link.Writer,
|
Writer: link.Writer,
|
||||||
Dest: destination,
|
Dest: destination,
|
||||||
|
T: signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
common.Interrupt(link.Reader)
|
||||||
|
}, 300*time.Second),
|
||||||
}
|
}
|
||||||
return bufio.CopyPacketConn(ctx, conn, outConn)
|
return bufio.CopyPacketConn(ctx, conn, outConn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||||
C "github.com/sagernet/sing/common"
|
C "github.com/sagernet/sing/common"
|
||||||
@@ -22,6 +23,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/signal"
|
||||||
"github.com/xtls/xray-core/common/singbridge"
|
"github.com/xtls/xray-core/common/singbridge"
|
||||||
"github.com/xtls/xray-core/common/uuid"
|
"github.com/xtls/xray-core/common/uuid"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
@@ -237,11 +239,10 @@ func (i *MultiUserInbound) NewConnection(ctx context.Context, conn net.Conn, met
|
|||||||
})
|
})
|
||||||
errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination)
|
errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination)
|
||||||
dispatcher := session.DispatcherFromContext(ctx)
|
dispatcher := session.DispatcherFromContext(ctx)
|
||||||
destination := singbridge.ToDestination(metadata.Destination, net.Network_TCP)
|
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_TCP)
|
||||||
if !destination.IsValid() {
|
if err != nil {
|
||||||
return errors.New("invalid destination")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
link, err := dispatcher.Dispatch(ctx, destination)
|
link, err := dispatcher.Dispatch(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -262,7 +263,10 @@ func (i *MultiUserInbound) NewPacketConnection(ctx context.Context, conn N.Packe
|
|||||||
})
|
})
|
||||||
errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination)
|
errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination)
|
||||||
dispatcher := session.DispatcherFromContext(ctx)
|
dispatcher := session.DispatcherFromContext(ctx)
|
||||||
destination := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
|
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
link, err := dispatcher.Dispatch(ctx, destination)
|
link, err := dispatcher.Dispatch(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -271,6 +275,9 @@ func (i *MultiUserInbound) NewPacketConnection(ctx context.Context, conn N.Packe
|
|||||||
Reader: link.Reader,
|
Reader: link.Reader,
|
||||||
Writer: link.Writer,
|
Writer: link.Writer,
|
||||||
Dest: destination,
|
Dest: destination,
|
||||||
|
T: signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
common.Interrupt(link.Reader)
|
||||||
|
}, 300*time.Second),
|
||||||
}
|
}
|
||||||
return bufio.CopyPacketConn(ctx, conn, outConn)
|
return bufio.CopyPacketConn(ctx, conn, outConn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||||
C "github.com/sagernet/sing/common"
|
C "github.com/sagernet/sing/common"
|
||||||
@@ -20,6 +21,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/signal"
|
||||||
"github.com/xtls/xray-core/common/singbridge"
|
"github.com/xtls/xray-core/common/singbridge"
|
||||||
"github.com/xtls/xray-core/common/uuid"
|
"github.com/xtls/xray-core/common/uuid"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
@@ -138,7 +140,11 @@ func (i *RelayInbound) NewConnection(ctx context.Context, conn net.Conn, metadat
|
|||||||
})
|
})
|
||||||
errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination)
|
errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination)
|
||||||
dispatcher := session.DispatcherFromContext(ctx)
|
dispatcher := session.DispatcherFromContext(ctx)
|
||||||
link, err := dispatcher.Dispatch(ctx, singbridge.ToDestination(metadata.Destination, net.Network_TCP))
|
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_TCP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
link, err := dispatcher.Dispatch(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -161,7 +167,10 @@ func (i *RelayInbound) NewPacketConnection(ctx context.Context, conn N.PacketCon
|
|||||||
})
|
})
|
||||||
errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination)
|
errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination)
|
||||||
dispatcher := session.DispatcherFromContext(ctx)
|
dispatcher := session.DispatcherFromContext(ctx)
|
||||||
destination := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
|
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
link, err := dispatcher.Dispatch(ctx, destination)
|
link, err := dispatcher.Dispatch(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -170,6 +179,9 @@ func (i *RelayInbound) NewPacketConnection(ctx context.Context, conn N.PacketCon
|
|||||||
Reader: link.Reader,
|
Reader: link.Reader,
|
||||||
Writer: link.Writer,
|
Writer: link.Writer,
|
||||||
Dest: destination,
|
Dest: destination,
|
||||||
|
T: signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
common.Interrupt(link.Reader)
|
||||||
|
}, 300*time.Second),
|
||||||
}
|
}
|
||||||
return bufio.CopyPacketConn(ctx, conn, outConn)
|
return bufio.CopyPacketConn(ctx, conn, outConn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/signal"
|
||||||
"github.com/xtls/xray-core/common/singbridge"
|
"github.com/xtls/xray-core/common/singbridge"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
@@ -142,6 +143,9 @@ func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer int
|
|||||||
Writer: link.Writer,
|
Writer: link.Writer,
|
||||||
Conn: inboundConn,
|
Conn: inboundConn,
|
||||||
Dest: destination,
|
Dest: destination,
|
||||||
|
T: signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
common.Interrupt(link.Reader)
|
||||||
|
}, 300*time.Second),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func (x *Fallback) GetXver() uint64 {
|
|||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Clients []*protocol.User `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"`
|
Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
|
||||||
Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"`
|
Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"`
|
||||||
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
|
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
|
||||||
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
||||||
@@ -149,9 +149,9 @@ func (*Config) Descriptor() ([]byte, []int) {
|
|||||||
return file_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{1}
|
return file_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetClients() []*protocol.User {
|
func (x *Config) GetUsers() []*protocol.User {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Clients
|
return x.Users
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -209,9 +209,9 @@ const file_proxy_vless_inbound_config_proto_rawDesc = "" +
|
|||||||
"\x04path\x18\x03 \x01(\tR\x04path\x12\x12\n" +
|
"\x04path\x18\x03 \x01(\tR\x04path\x12\x12\n" +
|
||||||
"\x04type\x18\x04 \x01(\tR\x04type\x12\x12\n" +
|
"\x04type\x18\x04 \x01(\tR\x04type\x12\x12\n" +
|
||||||
"\x04dest\x18\x05 \x01(\tR\x04dest\x12\x12\n" +
|
"\x04dest\x18\x05 \x01(\tR\x04dest\x12\x12\n" +
|
||||||
"\x04xver\x18\x06 \x01(\x04R\x04xver\"\x96\x02\n" +
|
"\x04xver\x18\x06 \x01(\x04R\x04xver\"\x92\x02\n" +
|
||||||
"\x06Config\x124\n" +
|
"\x06Config\x120\n" +
|
||||||
"\aclients\x18\x01 \x03(\v2\x1a.xray.common.protocol.UserR\aclients\x12@\n" +
|
"\x05users\x18\x01 \x03(\v2\x1a.xray.common.protocol.UserR\x05users\x12@\n" +
|
||||||
"\tfallbacks\x18\x02 \x03(\v2\".xray.proxy.vless.inbound.FallbackR\tfallbacks\x12\x1e\n" +
|
"\tfallbacks\x18\x02 \x03(\v2\".xray.proxy.vless.inbound.FallbackR\tfallbacks\x12\x1e\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"decryption\x18\x03 \x01(\tR\n" +
|
"decryption\x18\x03 \x01(\tR\n" +
|
||||||
@@ -242,7 +242,7 @@ var file_proxy_vless_inbound_config_proto_goTypes = []any{
|
|||||||
(*protocol.User)(nil), // 2: xray.common.protocol.User
|
(*protocol.User)(nil), // 2: xray.common.protocol.User
|
||||||
}
|
}
|
||||||
var file_proxy_vless_inbound_config_proto_depIdxs = []int32{
|
var file_proxy_vless_inbound_config_proto_depIdxs = []int32{
|
||||||
2, // 0: xray.proxy.vless.inbound.Config.clients:type_name -> xray.common.protocol.User
|
2, // 0: xray.proxy.vless.inbound.Config.users:type_name -> xray.common.protocol.User
|
||||||
0, // 1: xray.proxy.vless.inbound.Config.fallbacks:type_name -> xray.proxy.vless.inbound.Fallback
|
0, // 1: xray.proxy.vless.inbound.Config.fallbacks:type_name -> xray.proxy.vless.inbound.Fallback
|
||||||
2, // [2:2] is the sub-list for method output_type
|
2, // [2:2] is the sub-list for method output_type
|
||||||
2, // [2:2] is the sub-list for method input_type
|
2, // [2:2] is the sub-list for method input_type
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ message Fallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
repeated xray.common.protocol.User clients = 1;
|
repeated xray.common.protocol.User users = 1;
|
||||||
repeated Fallback fallbacks = 2;
|
repeated Fallback fallbacks = 2;
|
||||||
|
|
||||||
string decryption = 3;
|
string decryption = 3;
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func init() {
|
|||||||
c := config.(*Config)
|
c := config.(*Config)
|
||||||
|
|
||||||
validator := new(vless.MemoryValidator)
|
validator := new(vless.MemoryValidator)
|
||||||
for _, user := range c.Clients {
|
for _, user := range c.Users {
|
||||||
u, err := user.ToMemoryUser()
|
u, err := user.ToMemoryUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to get VLESS user").Base(err).AtError()
|
return nil, errors.New("failed to get VLESS user").Base(err).AtError()
|
||||||
|
|||||||
@@ -62,9 +62,9 @@ func TestCommanderListenConfigurationItem(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -145,9 +145,9 @@ func TestCommanderRemoveHandler(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -157,9 +157,9 @@ func TestCommanderRemoveHandler(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -240,9 +240,9 @@ func TestCommanderListHandlers(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -252,9 +252,9 @@ func TestCommanderListHandlers(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -379,9 +379,9 @@ func TestCommanderAddRemoveUser(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -416,9 +416,9 @@ func TestCommanderAddRemoveUser(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -567,9 +567,9 @@ func TestCommanderStats(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -591,9 +591,9 @@ func TestCommanderStats(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -87,9 +87,9 @@ func TestDokodemoTCP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -181,9 +181,9 @@ func TestDokodemoUDP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ func TestPassiveConnection(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -167,9 +167,9 @@ func TestProxy(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -301,9 +301,9 @@ func TestProxyOverKCP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -380,9 +380,9 @@ func TestBlackhole(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -391,9 +391,9 @@ func TestBlackhole(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest2.Address),
|
RewriteAddress: net.NewIPOrDomain(dest2.Address),
|
||||||
Port: uint32(dest2.Port),
|
RewritePort: uint32(dest2.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -506,9 +506,9 @@ func TestUDPConnection(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -552,9 +552,9 @@ func TestDomainSniffing(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
RewriteAddress: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: 443,
|
RewritePort: 443,
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ func TestMetrics(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ func TestVMessClosing(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -214,9 +214,9 @@ func TestZeroBuffer(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ func TestReverseProxy(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -142,9 +142,9 @@ func TestReverseProxy(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -252,9 +252,9 @@ func TestReverseProxyLongRunning(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -331,9 +331,9 @@ func TestReverseProxyLongRunning(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -103,9 +103,9 @@ func testShadowsocks2022Tcp(t *testing.T, method string, password string) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -188,9 +188,9 @@ func testShadowsocks2022Udp(t *testing.T, method string, password string) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(udpDest.Address),
|
RewriteAddress: net.NewIPOrDomain(udpDest.Address),
|
||||||
Port: uint32(udpDest.Port),
|
RewritePort: uint32(udpDest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,9 +68,9 @@ func TestShadowsocksChaCha20Poly1305TCP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -162,9 +162,9 @@ func TestShadowsocksAES256GCMTCP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -257,9 +257,9 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -351,9 +351,9 @@ func TestShadowsocksAES128GCMUDPMux(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -440,9 +440,9 @@ func TestShadowsocksNone(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ func TestSocksBridgeTCP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -143,9 +143,9 @@ func TestSocksWithHttpRequest(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -209,9 +209,9 @@ func TestSocksBridageUDP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -243,9 +243,9 @@ func TestSocksBridageUDP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -320,9 +320,9 @@ func TestSocksBridageUDPWithRouting(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -358,9 +358,9 @@ func TestSocksBridageUDPWithRouting(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -84,9 +84,9 @@ func TestSimpleTLSConnection(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -197,9 +197,9 @@ func TestAutoIssuingCertificate(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -302,9 +302,9 @@ func TestTLSOverKCP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -402,9 +402,9 @@ func TestTLSOverWebSocket(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -518,9 +518,9 @@ func TestGRPC(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -634,9 +634,9 @@ func TestGRPCMultiMode(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -743,9 +743,9 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -842,9 +842,9 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -940,9 +940,9 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1040,9 +1040,9 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ func TestHTTPConnectionHeader(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func TestVless(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
||||||
Clients: []*protocol.User{
|
Users: []*protocol.User{
|
||||||
{
|
{
|
||||||
Account: serial.ToTypedMessage(&vless.Account{
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
@@ -88,9 +88,9 @@ func TestVless(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -159,7 +159,7 @@ func TestVlessTls(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
||||||
Clients: []*protocol.User{
|
Users: []*protocol.User{
|
||||||
{
|
{
|
||||||
Account: serial.ToTypedMessage(&vless.Account{
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
@@ -193,9 +193,9 @@ func TestVlessTls(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -281,7 +281,7 @@ func TestVlessXtlsVision(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
||||||
Clients: []*protocol.User{
|
Users: []*protocol.User{
|
||||||
{
|
{
|
||||||
Account: serial.ToTypedMessage(&vless.Account{
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
@@ -316,9 +316,9 @@ func TestVlessXtlsVision(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -413,7 +413,7 @@ func TestVlessXtlsVisionReality(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
||||||
Clients: []*protocol.User{
|
Users: []*protocol.User{
|
||||||
{
|
{
|
||||||
Account: serial.ToTypedMessage(&vless.Account{
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
@@ -448,9 +448,9 @@ func TestVlessXtlsVisionReality(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -553,7 +553,7 @@ func TestVlessRealityFingerprints(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
ProxySettings: serial.ToTypedMessage(&inbound.Config{
|
||||||
Clients: []*protocol.User{
|
Users: []*protocol.User{
|
||||||
{
|
{
|
||||||
Account: serial.ToTypedMessage(&vless.Account{
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
@@ -586,9 +586,9 @@ func TestVlessRealityFingerprints(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -83,9 +83,9 @@ func TestVMessGCM(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -183,9 +183,9 @@ func TestVMessGCMReadv(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -286,9 +286,9 @@ func TestVMessGCMUDP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -383,9 +383,9 @@ func TestVMessChacha20(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -481,9 +481,9 @@ func TestVMessNone(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -581,9 +581,9 @@ func TestVMessKCP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -686,9 +686,9 @@ func TestVMessKCPLarge(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -792,9 +792,9 @@ func TestVMessGCMMux(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -906,9 +906,9 @@ func TestVMessGCMMuxUDP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -917,9 +917,9 @@ func TestVMessGCMMuxUDP(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(udpDest.Address),
|
RewriteAddress: net.NewIPOrDomain(udpDest.Address),
|
||||||
Port: uint32(udpDest.Port),
|
RewritePort: uint32(udpDest.Port),
|
||||||
Networks: []net.Network{net.Network_UDP},
|
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1028,9 +1028,9 @@ func TestVMessZero(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1125,9 +1125,9 @@ func TestVMessGCMLengthAuth(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1227,9 +1227,9 @@ func TestVMessGCMLengthAuthPlusNoTerminationSignal(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -84,9 +84,9 @@ func TestWireguard(t *testing.T) {
|
|||||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: net.NewIPOrDomain(dest.Address),
|
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||||
Port: uint32(dest.Port),
|
RewritePort: uint32(dest.Port),
|
||||||
Networks: []net.Network{net.Network_TCP},
|
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ func runVLESSRealityCase(t *testing.T, bin string, mode trafficMode, payloadSize
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&vin.Config{
|
ProxySettings: serial.ToTypedMessage(&vin.Config{
|
||||||
Clients: []*protocol.User{
|
Users: []*protocol.User{
|
||||||
{
|
{
|
||||||
Account: serial.ToTypedMessage(&vless.Account{
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
@@ -292,9 +292,9 @@ func runVLESSRealityCase(t *testing.T, bin string, mode trafficMode, payloadSize
|
|||||||
Listen: xnet.NewIPOrDomain(xnet.LocalHostIP),
|
Listen: xnet.NewIPOrDomain(xnet.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: xnet.NewIPOrDomain(backend.Address()),
|
RewriteAddress: xnet.NewIPOrDomain(backend.Address()),
|
||||||
Port: uint32(backend.Port()),
|
RewritePort: uint32(backend.Port()),
|
||||||
Networks: []xnet.Network{xnet.Network_TCP},
|
AllowedNetworks: []xnet.Network{xnet.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -412,9 +412,9 @@ func runHysteria2Case(t *testing.T, bin string, mode trafficMode, payloadSize in
|
|||||||
Listen: xnet.NewIPOrDomain(xnet.LocalHostIP),
|
Listen: xnet.NewIPOrDomain(xnet.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: xnet.NewIPOrDomain(backend.Address()),
|
RewriteAddress: xnet.NewIPOrDomain(backend.Address()),
|
||||||
Port: uint32(backend.Port()),
|
RewritePort: uint32(backend.Port()),
|
||||||
Networks: []xnet.Network{xnet.Network_TCP},
|
AllowedNetworks: []xnet.Network{xnet.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -501,7 +501,7 @@ func runVLesseEncCase(t *testing.T, bin string, mode trafficMode, payloadSize in
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&vin.Config{
|
ProxySettings: serial.ToTypedMessage(&vin.Config{
|
||||||
Clients: []*protocol.User{
|
Users: []*protocol.User{
|
||||||
{
|
{
|
||||||
Account: serial.ToTypedMessage(&vless.Account{
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
@@ -530,9 +530,9 @@ func runVLesseEncCase(t *testing.T, bin string, mode trafficMode, payloadSize in
|
|||||||
Listen: xnet.NewIPOrDomain(xnet.LocalHostIP),
|
Listen: xnet.NewIPOrDomain(xnet.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: xnet.NewIPOrDomain(backend.Address()),
|
RewriteAddress: xnet.NewIPOrDomain(backend.Address()),
|
||||||
Port: uint32(backend.Port()),
|
RewritePort: uint32(backend.Port()),
|
||||||
Networks: []xnet.Network{xnet.Network_TCP},
|
AllowedNetworks: []xnet.Network{xnet.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -605,7 +605,7 @@ func runVLESSXHTTPCase(t *testing.T, bin string, mode trafficMode, payloadSize i
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&vin.Config{
|
ProxySettings: serial.ToTypedMessage(&vin.Config{
|
||||||
Clients: []*protocol.User{
|
Users: []*protocol.User{
|
||||||
{
|
{
|
||||||
Account: serial.ToTypedMessage(&vless.Account{
|
Account: serial.ToTypedMessage(&vless.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
@@ -630,9 +630,9 @@ func runVLESSXHTTPCase(t *testing.T, bin string, mode trafficMode, payloadSize i
|
|||||||
Listen: xnet.NewIPOrDomain(xnet.LocalHostIP),
|
Listen: xnet.NewIPOrDomain(xnet.LocalHostIP),
|
||||||
}),
|
}),
|
||||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
Address: xnet.NewIPOrDomain(backend.Address()),
|
RewriteAddress: xnet.NewIPOrDomain(backend.Address()),
|
||||||
Port: uint32(backend.Port()),
|
RewritePort: uint32(backend.Port()),
|
||||||
Networks: []xnet.Network{xnet.Network_TCP},
|
AllowedNetworks: []xnet.Network{xnet.Network_TCP},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -40,6 +39,7 @@ type xdnsConnClient struct {
|
|||||||
net.PacketConn
|
net.PacketConn
|
||||||
|
|
||||||
resolverAddrs []*net.UDPAddr
|
resolverAddrs []*net.UDPAddr
|
||||||
|
resolverTypes []uint16
|
||||||
resolverIdx uint32
|
resolverIdx uint32
|
||||||
resolverSend map[string]*atomic.Uint32
|
resolverSend map[string]*atomic.Uint32
|
||||||
|
|
||||||
@@ -61,17 +61,15 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
|||||||
|
|
||||||
var domains []Name
|
var domains []Name
|
||||||
var servers []string
|
var servers []string
|
||||||
|
var resolverTypes []uint16
|
||||||
for _, rs := range c.Resolvers {
|
for _, rs := range c.Resolvers {
|
||||||
parts := strings.Split(rs, "+udp://")
|
domain, server, resolverType, err := parseResolver(rs)
|
||||||
if len(parts) != 2 {
|
|
||||||
return nil, errors.New("invalid resolvers")
|
|
||||||
}
|
|
||||||
domain, err := ParseName(parts[0])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.New("invalid resolvers").Base(err)
|
||||||
}
|
}
|
||||||
domains = append(domains, domain)
|
domains = append(domains, domain)
|
||||||
servers = append(servers, parts[1])
|
servers = append(servers, server)
|
||||||
|
resolverTypes = append(resolverTypes, resolverType)
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolverAddrs []*net.UDPAddr
|
var resolverAddrs []*net.UDPAddr
|
||||||
@@ -85,9 +83,9 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
|||||||
if ip == nil {
|
if ip == nil {
|
||||||
return nil, errors.New("invalid ip address")
|
return nil, errors.New("invalid ip address")
|
||||||
}
|
}
|
||||||
port, _ := strconv.Atoi(p)
|
port, err := strconv.Atoi(p)
|
||||||
if port == 0 {
|
if err != nil {
|
||||||
return nil, errors.New("invalid port")
|
return nil, errors.New("invalid port").Base(err)
|
||||||
}
|
}
|
||||||
addr := &net.UDPAddr{IP: ip, Port: port}
|
addr := &net.UDPAddr{IP: ip, Port: port}
|
||||||
resolverAddrs = append(resolverAddrs, addr)
|
resolverAddrs = append(resolverAddrs, addr)
|
||||||
@@ -98,6 +96,7 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
|||||||
PacketConn: raw,
|
PacketConn: raw,
|
||||||
|
|
||||||
resolverAddrs: resolverAddrs,
|
resolverAddrs: resolverAddrs,
|
||||||
|
resolverTypes: resolverTypes,
|
||||||
resolverIdx: 0,
|
resolverIdx: 0,
|
||||||
resolverSend: resolverSend,
|
resolverSend: resolverSend,
|
||||||
|
|
||||||
@@ -216,7 +215,7 @@ func (c *xdnsConnClient) sendLoop() {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
encoded, _ := encode(nil, c.clientID, c.domains[c.resolverIdx])
|
encoded, _ := encode(nil, c.clientID, c.domains[c.resolverIdx], c.resolverTypes[c.resolverIdx])
|
||||||
p = &packet{
|
p = &packet{
|
||||||
p: encoded,
|
p: encoded,
|
||||||
}
|
}
|
||||||
@@ -276,7 +275,8 @@ func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
}
|
}
|
||||||
|
|
||||||
encoded, err := encode(p, c.clientID, c.domains[c.resolverIdx%uint32(len(c.resolverAddrs))])
|
idx := c.resolverIdx % uint32(len(c.resolverAddrs))
|
||||||
|
encoded, err := encode(p, c.clientID, c.domains[idx], c.resolverTypes[idx])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogDebug(context.Background(), addr, " xdns wireformat err ", err, " ", len(p))
|
errors.LogDebug(context.Background(), addr, " xdns wireformat err ", err, " ", len(p))
|
||||||
return 0, nil
|
return 0, nil
|
||||||
@@ -299,7 +299,7 @@ func (c *xdnsConnClient) Close() error {
|
|||||||
return c.PacketConn.Close()
|
return c.PacketConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func encode(p []byte, clientID []byte, domain Name) ([]byte, error) {
|
func encode(p []byte, clientID []byte, domain Name, qtype uint16) ([]byte, error) {
|
||||||
var decoded []byte
|
var decoded []byte
|
||||||
{
|
{
|
||||||
if len(p) >= 224 {
|
if len(p) >= 224 {
|
||||||
@@ -338,7 +338,7 @@ func encode(p []byte, clientID []byte, domain Name) ([]byte, error) {
|
|||||||
Question: []Question{
|
Question: []Question{
|
||||||
{
|
{
|
||||||
Name: name,
|
Name: name,
|
||||||
Type: RRTypeTXT,
|
Type: qtype,
|
||||||
Class: ClassIN,
|
Class: ClassIN,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -396,29 +396,22 @@ func dnsResponsePayload(resp *Message, domains []Name) []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(resp.Answer) != 1 {
|
if len(resp.Answer) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
answer := resp.Answer[0]
|
|
||||||
|
|
||||||
var ok bool
|
for _, answer := range resp.Answer {
|
||||||
for _, domain := range domains {
|
var ok bool
|
||||||
_, ok = answer.Name.TrimSuffix(domain)
|
for _, domain := range domains {
|
||||||
if ok {
|
_, ok = answer.Name.TrimSuffix(domain)
|
||||||
break
|
if ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if answer.Type != RRTypeTXT {
|
return decodeResponsePayload(resp.Answer)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
payload, err := DecodeRDataTXT(answer.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return payload
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,8 +45,14 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.2.2
|
||||||
|
RRTypeA = 1
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.2.2
|
||||||
|
RRTypeCNAME = 5
|
||||||
// https://tools.ietf.org/html/rfc1035#section-3.2.2
|
// https://tools.ietf.org/html/rfc1035#section-3.2.2
|
||||||
RRTypeTXT = 16
|
RRTypeTXT = 16
|
||||||
|
// https://tools.ietf.org/html/rfc3596#section-2.1
|
||||||
|
RRTypeAAAA = 28
|
||||||
// https://tools.ietf.org/html/rfc6891#section-6.1.1
|
// https://tools.ietf.org/html/rfc6891#section-6.1.1
|
||||||
RRTypeOPT = 41
|
RRTypeOPT = 41
|
||||||
|
|
||||||
|
|||||||
@@ -593,3 +593,113 @@ func TestRDataTXTRoundTrip(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIPAnswerPayloadRoundTrip(t *testing.T) {
|
||||||
|
for _, rrType := range []uint16{RRTypeA, RRTypeAAAA} {
|
||||||
|
for _, payload := range [][]byte{
|
||||||
|
{},
|
||||||
|
{0x01},
|
||||||
|
[]byte("hello world"),
|
||||||
|
bytes.Repeat([]byte{0xab}, payloadChunkSizeForType(rrType)*3+1),
|
||||||
|
} {
|
||||||
|
question := Question{
|
||||||
|
Name: mustParseName("example.com"),
|
||||||
|
Type: rrType,
|
||||||
|
Class: ClassIN,
|
||||||
|
}
|
||||||
|
answers, err := answersForPayload(question, responseTTL, payload)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("answersForPayload(%d) err = %v", rrType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(answers) > 1 {
|
||||||
|
answers[0], answers[len(answers)-1] = answers[len(answers)-1], answers[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded := decodeResponsePayload(answers)
|
||||||
|
if !bytes.Equal(decoded, payload) {
|
||||||
|
t.Fatalf("rrType=%d decoded %x want %x", rrType, decoded, payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseResolver(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
resolver string
|
||||||
|
rrType uint16
|
||||||
|
}{
|
||||||
|
{"example.com+udp://1.1.1.1:53", RRTypeTXT},
|
||||||
|
{"example.com:txt+udp://1.1.1.1:53", RRTypeTXT},
|
||||||
|
{"example.com:a+udp://1.1.1.1:53", RRTypeA},
|
||||||
|
{"example.com:aaaa+udp://1.1.1.1:53", RRTypeAAAA},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
domain, server, rrType, err := parseResolver(test.resolver)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parseResolver(%q) err = %v", test.resolver, err)
|
||||||
|
}
|
||||||
|
if domain.String() != "example.com" || server != "1.1.1.1:53" || rrType != test.rrType {
|
||||||
|
t.Fatalf("parseResolver(%q) = (%q, %q, %d)", test.resolver, domain.String(), server, rrType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseDomainSpec(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
spec string
|
||||||
|
def string
|
||||||
|
rrType uint16
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"example.com", "", 0, false},
|
||||||
|
{"example.com", "txt", RRTypeTXT, false},
|
||||||
|
{"example.com:a", "", RRTypeA, false},
|
||||||
|
{"example.com:aaaa", "", RRTypeAAAA, false},
|
||||||
|
{"example.com:doh", "", 0, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
got, err := parseDomainSpec(test.spec, test.def)
|
||||||
|
if test.wantErr {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("parseDomainSpec(%q, %q) err = nil", test.spec, test.def)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parseDomainSpec(%q, %q) err = %v", test.spec, test.def, err)
|
||||||
|
}
|
||||||
|
if got.name.String() != "example.com" || got.rrType != test.rrType {
|
||||||
|
t.Fatalf("parseDomainSpec(%q, %q) = (%q, %d)", test.spec, test.def, got.name.String(), got.rrType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponseForMethodRestriction(t *testing.T) {
|
||||||
|
query := &Message{
|
||||||
|
ID: 1,
|
||||||
|
Flags: 0x0100,
|
||||||
|
Question: []Question{{
|
||||||
|
Name: mustParseName("abc.example.com"),
|
||||||
|
Type: RRTypeTXT,
|
||||||
|
Class: ClassIN,
|
||||||
|
}},
|
||||||
|
Additional: []RR{{
|
||||||
|
Name: Name{},
|
||||||
|
Type: RRTypeOPT,
|
||||||
|
Class: 4096,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, _ := responseFor(query, []domainSpec{{name: mustParseName("example.com"), rrType: RRTypeA}})
|
||||||
|
if resp == nil || resp.Rcode() != RcodeNameError {
|
||||||
|
t.Fatalf("responseFor method restriction rcode = %v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, _ = responseFor(query, []domainSpec{{name: mustParseName("example.com")}})
|
||||||
|
if resp == nil || resp.Rcode() != RcodeNoError {
|
||||||
|
t.Fatalf("responseFor unrestricted rcode = %v", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
package xdns
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
const ipRecordHeaderSize = 2
|
||||||
|
|
||||||
|
func maxEncodedPayloadForType(rrType uint16) int {
|
||||||
|
switch rrType {
|
||||||
|
case RRTypeA:
|
||||||
|
return maxEncodedPayloadA
|
||||||
|
case RRTypeAAAA:
|
||||||
|
return maxEncodedPayloadAAAA
|
||||||
|
default:
|
||||||
|
return maxEncodedPayloadTXT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rrDataSizeForType(rrType uint16) int {
|
||||||
|
switch rrType {
|
||||||
|
case RRTypeA:
|
||||||
|
return 4
|
||||||
|
case RRTypeAAAA:
|
||||||
|
return 16
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func payloadChunkSizeForType(rrType uint16) int {
|
||||||
|
size := rrDataSizeForType(rrType)
|
||||||
|
if size <= ipRecordHeaderSize {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return size - ipRecordHeaderSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func answersForPayload(question Question, ttl uint32, payload []byte) ([]RR, error) {
|
||||||
|
switch question.Type {
|
||||||
|
case RRTypeTXT:
|
||||||
|
return []RR{
|
||||||
|
{
|
||||||
|
Name: question.Name,
|
||||||
|
Type: question.Type,
|
||||||
|
Class: question.Class,
|
||||||
|
TTL: ttl,
|
||||||
|
Data: EncodeRDataTXT(payload),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
case RRTypeA, RRTypeAAAA:
|
||||||
|
return ipAnswersForPayload(question, ttl, payload)
|
||||||
|
default:
|
||||||
|
return nil, ErrIntegerOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipAnswersForPayload(question Question, ttl uint32, payload []byte) ([]RR, error) {
|
||||||
|
chunkSize := payloadChunkSizeForType(question.Type)
|
||||||
|
rrDataSize := rrDataSizeForType(question.Type)
|
||||||
|
if chunkSize == 0 || rrDataSize == 0 {
|
||||||
|
return nil, ErrIntegerOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
numRecords := 1
|
||||||
|
if len(payload) > 0 {
|
||||||
|
numRecords = (len(payload) + chunkSize - 1) / chunkSize
|
||||||
|
}
|
||||||
|
if numRecords > 256 {
|
||||||
|
return nil, ErrIntegerOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
answers := make([]RR, 0, numRecords)
|
||||||
|
for i := 0; i < numRecords; i++ {
|
||||||
|
offset := i * chunkSize
|
||||||
|
n := len(payload) - offset
|
||||||
|
if n < 0 {
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
if n > chunkSize {
|
||||||
|
n = chunkSize
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, rrDataSize)
|
||||||
|
data[0] = byte(i)
|
||||||
|
data[1] = byte(n)
|
||||||
|
copy(data[ipRecordHeaderSize:], payload[offset:offset+n])
|
||||||
|
|
||||||
|
answers = append(answers, RR{
|
||||||
|
Name: question.Name,
|
||||||
|
Type: question.Type,
|
||||||
|
Class: question.Class,
|
||||||
|
TTL: ttl,
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return answers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeResponsePayload(answers []RR) []byte {
|
||||||
|
if len(answers) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch answers[0].Type {
|
||||||
|
case RRTypeTXT:
|
||||||
|
if len(answers) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
payload, err := DecodeRDataTXT(answers[0].Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return payload
|
||||||
|
case RRTypeA, RRTypeAAAA:
|
||||||
|
return decodeIPAnswerPayload(answers, answers[0].Type)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeIPAnswerPayload(answers []RR, rrType uint16) []byte {
|
||||||
|
chunkSize := payloadChunkSizeForType(rrType)
|
||||||
|
rrDataSize := rrDataSizeForType(rrType)
|
||||||
|
if chunkSize == 0 || rrDataSize == 0 || len(answers) > 256 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := make([][]byte, len(answers))
|
||||||
|
for _, answer := range answers {
|
||||||
|
if answer.Type != rrType || len(answer.Data) != rrDataSize {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
idx := int(answer.Data[0])
|
||||||
|
n := int(answer.Data[1])
|
||||||
|
if idx >= len(answers) || n > chunkSize || parts[idx] != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
part := make([]byte, n)
|
||||||
|
copy(part, answer.Data[ipRecordHeaderSize:ipRecordHeaderSize+n])
|
||||||
|
parts[idx] = part
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload bytes.Buffer
|
||||||
|
for _, part := range parts {
|
||||||
|
if part == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
payload.Write(part)
|
||||||
|
}
|
||||||
|
return payload.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeMaxEncodedPayload(limit int) int {
|
||||||
|
return computeMaxEncodedPayloadForType(limit, RRTypeTXT)
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeMaxEncodedPayloadForType(limit int, rrType uint16) int {
|
||||||
|
maxLengthName, err := NewName([][]byte{
|
||||||
|
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
||||||
|
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
||||||
|
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
||||||
|
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
n := 0
|
||||||
|
for _, label := range maxLengthName {
|
||||||
|
n += len(label) + 1
|
||||||
|
}
|
||||||
|
n += 1
|
||||||
|
if n != 255 {
|
||||||
|
panic("computeMaxEncodedPayload n != 255")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queryLimit := uint16(limit)
|
||||||
|
if int(queryLimit) != limit {
|
||||||
|
queryLimit = 0xffff
|
||||||
|
}
|
||||||
|
query := &Message{
|
||||||
|
Question: []Question{
|
||||||
|
{
|
||||||
|
Name: maxLengthName,
|
||||||
|
Type: rrType,
|
||||||
|
Class: ClassIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Additional: []RR{
|
||||||
|
{
|
||||||
|
Name: Name{},
|
||||||
|
Type: RRTypeOPT,
|
||||||
|
Class: queryLimit,
|
||||||
|
TTL: 0,
|
||||||
|
Data: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp, _ := responseFor(query, []domainSpec{{name: Name{[]byte{}}}})
|
||||||
|
|
||||||
|
low := 0
|
||||||
|
high := 32768
|
||||||
|
if chunkSize := payloadChunkSizeForType(rrType); chunkSize > 0 {
|
||||||
|
high = 256*chunkSize + 1
|
||||||
|
}
|
||||||
|
for low+1 < high {
|
||||||
|
mid := (low + high) / 2
|
||||||
|
resp.Answer, err = answersForPayload(query.Question[0], responseTTL, make([]byte, mid))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
buf, err := resp.WireFormat()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if len(buf) <= limit {
|
||||||
|
low = mid
|
||||||
|
} else {
|
||||||
|
high = mid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return low
|
||||||
|
}
|
||||||
@@ -21,8 +21,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
maxUDPPayload = 1280 - 40 - 8
|
maxUDPPayload = 1280 - 40 - 8
|
||||||
maxEncodedPayload = computeMaxEncodedPayload(maxUDPPayload)
|
maxEncodedPayloadTXT = computeMaxEncodedPayloadForType(maxUDPPayload, RRTypeTXT)
|
||||||
|
maxEncodedPayloadA = computeMaxEncodedPayloadForType(maxUDPPayload, RRTypeA)
|
||||||
|
maxEncodedPayloadAAAA = computeMaxEncodedPayloadForType(maxUDPPayload, RRTypeAAAA)
|
||||||
)
|
)
|
||||||
|
|
||||||
func clientIDToAddr(clientID [8]byte) *net.UDPAddr {
|
func clientIDToAddr(clientID [8]byte) *net.UDPAddr {
|
||||||
@@ -44,15 +46,16 @@ type record struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type queue struct {
|
type queue struct {
|
||||||
last time.Time
|
last time.Time
|
||||||
queue chan []byte
|
rrType uint16
|
||||||
stash chan []byte
|
queue chan []byte
|
||||||
|
stash chan []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type xdnsConnServer struct {
|
type xdnsConnServer struct {
|
||||||
net.PacketConn
|
net.PacketConn
|
||||||
|
|
||||||
domains []Name
|
domains []domainSpec
|
||||||
|
|
||||||
ch chan *record
|
ch chan *record
|
||||||
readQueue chan *packet
|
readQueue chan *packet
|
||||||
@@ -66,9 +69,9 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
|||||||
if len(c.Domains) == 0 {
|
if len(c.Domains) == 0 {
|
||||||
return nil, errors.New("empty domains")
|
return nil, errors.New("empty domains")
|
||||||
}
|
}
|
||||||
domains := make([]Name, 0, len(c.Domains))
|
domains := make([]domainSpec, 0, len(c.Domains))
|
||||||
for _, domain := range c.Domains {
|
for _, domain := range c.Domains {
|
||||||
domain, err := ParseName(domain)
|
domain, err := parseDomainSpec(domain, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -234,6 +237,7 @@ func (c *xdnsConnServer) recvLoop() {
|
|||||||
func (c *xdnsConnServer) sendLoop() {
|
func (c *xdnsConnServer) sendLoop() {
|
||||||
var nextRec *record
|
var nextRec *record
|
||||||
for {
|
for {
|
||||||
|
var err error
|
||||||
rec := nextRec
|
rec := nextRec
|
||||||
nextRec = nil
|
nextRec = nil
|
||||||
|
|
||||||
@@ -246,18 +250,8 @@ func (c *xdnsConnServer) sendLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if rec.Resp.Rcode() == RcodeNoError && len(rec.Resp.Question) == 1 {
|
if rec.Resp.Rcode() == RcodeNoError && len(rec.Resp.Question) == 1 {
|
||||||
rec.Resp.Answer = []RR{
|
|
||||||
{
|
|
||||||
Name: rec.Resp.Question[0].Name,
|
|
||||||
Type: rec.Resp.Question[0].Type,
|
|
||||||
Class: rec.Resp.Question[0].Class,
|
|
||||||
TTL: responseTTL,
|
|
||||||
Data: nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var payload bytes.Buffer
|
var payload bytes.Buffer
|
||||||
limit := maxEncodedPayload
|
limit := maxEncodedPayloadForType(rec.Resp.Question[0].Type)
|
||||||
timer := time.NewTimer(maxResponseDelay)
|
timer := time.NewTimer(maxResponseDelay)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -267,6 +261,7 @@ func (c *xdnsConnServer) sendLoop() {
|
|||||||
c.mutex.Unlock()
|
c.mutex.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
q.rrType = rec.Resp.Question[0].Type
|
||||||
c.mutex.Unlock()
|
c.mutex.Unlock()
|
||||||
|
|
||||||
var p []byte
|
var p []byte
|
||||||
@@ -294,7 +289,11 @@ func (c *xdnsConnServer) sendLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
limit -= 2 + len(p)
|
limit -= 2 + len(p)
|
||||||
if payload.Len() > 0 && limit < 0 {
|
if limit < 0 {
|
||||||
|
if payload.Len() == 0 {
|
||||||
|
errors.LogDebug(context.Background(), rec.Addr, " ", rec.ClientAddr, " xdns payload too large for rrtype ", rec.Resp.Question[0].Type, " ", len(p))
|
||||||
|
continue
|
||||||
|
}
|
||||||
c.stash(q, p)
|
c.stash(q, p)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -308,7 +307,11 @@ func (c *xdnsConnServer) sendLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
rec.Resp.Answer[0].Data = EncodeRDataTXT(payload.Bytes())
|
rec.Resp.Answer, err = answersForPayload(rec.Resp.Question[0], responseTTL, payload.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
errors.LogDebug(context.Background(), rec.Addr, " ", rec.ClientAddr, " xdns encode err ", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := rec.Resp.WireFormat()
|
buf, err := rec.Resp.WireFormat()
|
||||||
@@ -349,11 +352,6 @@ func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
if len(p)+2 > maxEncodedPayload {
|
|
||||||
errors.LogDebug(context.Background(), addr, " mask write err short write ", len(p), "+2 > ", maxEncodedPayload)
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
defer c.mutex.Unlock()
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
@@ -361,6 +359,14 @@ func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|||||||
if q == nil {
|
if q == nil {
|
||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
}
|
}
|
||||||
|
limit := maxEncodedPayloadForType(q.rrType)
|
||||||
|
if q.rrType == 0 {
|
||||||
|
limit = maxEncodedPayloadTXT
|
||||||
|
}
|
||||||
|
if len(p)+2 > limit {
|
||||||
|
errors.LogDebug(context.Background(), addr, " mask write err short write ", len(p), "+2 > ", limit)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
buf := make([]byte, len(p))
|
buf := make([]byte, len(p))
|
||||||
copy(buf, p)
|
copy(buf, p)
|
||||||
@@ -406,7 +412,7 @@ func nextPacketServer(r *bytes.Reader) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func responseFor(query *Message, domains []Name) (*Message, []byte) {
|
func responseFor(query *Message, domains []domainSpec) (*Message, []byte) {
|
||||||
resp := &Message{
|
resp := &Message{
|
||||||
ID: query.ID,
|
ID: query.ID,
|
||||||
Flags: 0x8000,
|
Flags: 0x8000,
|
||||||
@@ -454,11 +460,15 @@ func responseFor(query *Message, domains []Name) (*Message, []byte) {
|
|||||||
}
|
}
|
||||||
question := query.Question[0]
|
question := query.Question[0]
|
||||||
|
|
||||||
var prefix Name
|
var (
|
||||||
var ok bool
|
prefix Name
|
||||||
|
ok bool
|
||||||
|
match domainSpec
|
||||||
|
)
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
prefix, ok = question.Name.TrimSuffix(domain)
|
prefix, ok = question.Name.TrimSuffix(domain.name)
|
||||||
if ok {
|
if ok {
|
||||||
|
match = domain
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -473,7 +483,13 @@ func responseFor(query *Message, domains []Name) (*Message, []byte) {
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if question.Type != RRTypeTXT {
|
switch question.Type {
|
||||||
|
case RRTypeTXT, RRTypeA, RRTypeAAAA:
|
||||||
|
default:
|
||||||
|
resp.Flags |= RcodeNameError
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
if match.rrType != 0 && question.Type != match.rrType {
|
||||||
resp.Flags |= RcodeNameError
|
resp.Flags |= RcodeNameError
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
@@ -494,78 +510,3 @@ func responseFor(query *Message, domains []Name) (*Message, []byte) {
|
|||||||
|
|
||||||
return resp, payload
|
return resp, payload
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeMaxEncodedPayload(limit int) int {
|
|
||||||
maxLengthName, err := NewName([][]byte{
|
|
||||||
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
|
||||||
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
|
||||||
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
|
||||||
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
n := 0
|
|
||||||
for _, label := range maxLengthName {
|
|
||||||
n += len(label) + 1
|
|
||||||
}
|
|
||||||
n += 1
|
|
||||||
if n != 255 {
|
|
||||||
panic("computeMaxEncodedPayload n != 255")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
queryLimit := uint16(limit)
|
|
||||||
if int(queryLimit) != limit {
|
|
||||||
queryLimit = 0xffff
|
|
||||||
}
|
|
||||||
query := &Message{
|
|
||||||
Question: []Question{
|
|
||||||
{
|
|
||||||
Name: maxLengthName,
|
|
||||||
Type: RRTypeTXT,
|
|
||||||
Class: RRTypeTXT,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
Additional: []RR{
|
|
||||||
{
|
|
||||||
Name: Name{},
|
|
||||||
Type: RRTypeOPT,
|
|
||||||
Class: queryLimit,
|
|
||||||
TTL: 0,
|
|
||||||
Data: []byte{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
resp, _ := responseFor(query, []Name{[][]byte{}})
|
|
||||||
|
|
||||||
resp.Answer = []RR{
|
|
||||||
{
|
|
||||||
Name: query.Question[0].Name,
|
|
||||||
Type: query.Question[0].Type,
|
|
||||||
Class: query.Question[0].Class,
|
|
||||||
TTL: responseTTL,
|
|
||||||
Data: nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
low := 0
|
|
||||||
high := 32768
|
|
||||||
for low+1 < high {
|
|
||||||
mid := (low + high) / 2
|
|
||||||
resp.Answer[0].Data = EncodeRDataTXT(make([]byte, mid))
|
|
||||||
buf, err := resp.WireFormat()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if len(buf) <= limit {
|
|
||||||
low = mid
|
|
||||||
} else {
|
|
||||||
high = mid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return low
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package xdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type domainSpec struct {
|
||||||
|
name Name
|
||||||
|
rrType uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func rrTypeFromMethod(method string) (uint16, error) {
|
||||||
|
switch strings.ToLower(method) {
|
||||||
|
case "", "txt":
|
||||||
|
return RRTypeTXT, nil
|
||||||
|
case "a":
|
||||||
|
return RRTypeA, nil
|
||||||
|
case "aaaa":
|
||||||
|
return RRTypeAAAA, nil
|
||||||
|
default:
|
||||||
|
return 0, errors.New("unsupported method")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDomainSpec(s string, defaultMethod string) (domainSpec, error) {
|
||||||
|
domainPart := s
|
||||||
|
method := ""
|
||||||
|
hasMethod := false
|
||||||
|
|
||||||
|
if i := strings.LastIndex(s, ":"); i >= 0 {
|
||||||
|
domainPart = s[:i]
|
||||||
|
method = s[i+1:]
|
||||||
|
hasMethod = true
|
||||||
|
} else if defaultMethod != "" {
|
||||||
|
method = defaultMethod
|
||||||
|
hasMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if domainPart == "" {
|
||||||
|
return domainSpec{}, errors.New("empty domain")
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := ParseName(domainPart)
|
||||||
|
if err != nil {
|
||||||
|
return domainSpec{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rrType := uint16(0)
|
||||||
|
if hasMethod {
|
||||||
|
var err error
|
||||||
|
rrType, err = rrTypeFromMethod(method)
|
||||||
|
if err != nil {
|
||||||
|
return domainSpec{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainSpec{
|
||||||
|
name: name,
|
||||||
|
rrType: rrType,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseResolver(s string) (Name, string, uint16, error) {
|
||||||
|
head, server, ok := strings.Cut(s, "+udp://")
|
||||||
|
if !ok {
|
||||||
|
return nil, "", 0, errors.New("invalid resolver scheme")
|
||||||
|
}
|
||||||
|
if server == "" {
|
||||||
|
return nil, "", 0, errors.New("empty resolver server")
|
||||||
|
}
|
||||||
|
|
||||||
|
spec, err := parseDomainSpec(head, "txt")
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec.name, server, spec.rrType, nil
|
||||||
|
}
|
||||||
@@ -309,7 +309,7 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
|
|||||||
|
|
||||||
tr := &quic.Transport{Conn: pktConn}
|
tr := &quic.Transport{Conn: pktConn}
|
||||||
|
|
||||||
listener, err := tr.Listen(tlsConfig.GetTLSConfig(), quicConfig)
|
listener, err := tr.Listen(tlsConfig.GetTLSConfig(tls.WithNextProto("h3")), quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tr.Close()
|
_ = tr.Close()
|
||||||
_ = pktConn.Close()
|
_ = pktConn.Close()
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, sessio
|
|||||||
errors.LogInfoInner(ctx, err, "failed to "+method+" "+url)
|
errors.LogInfoInner(ctx, err, "failed to "+method+" "+url)
|
||||||
}
|
}
|
||||||
gotConn.Close()
|
gotConn.Close()
|
||||||
|
common.Close(body)
|
||||||
wrc.Close()
|
wrc.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -80,6 +81,7 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, sessio
|
|||||||
if resp.StatusCode != 200 || uploadOnly { // stream-up
|
if resp.StatusCode != 200 || uploadOnly { // stream-up
|
||||||
io.Copy(io.Discard, resp.Body)
|
io.Copy(io.Discard, resp.Body)
|
||||||
resp.Body.Close() // if it is called immediately, the upload will be interrupted also
|
resp.Body.Close() // if it is called immediately, the upload will be interrupted also
|
||||||
|
common.Close(body)
|
||||||
wrc.Close()
|
wrc.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,6 +183,8 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
if quicParams.KeepAlivePeriod == 0 {
|
if quicParams.KeepAlivePeriod == 0 {
|
||||||
if keepAlivePeriod == 0 {
|
if keepAlivePeriod == 0 {
|
||||||
quicConfig.KeepAlivePeriod = net.QuicgoH3KeepAlivePeriod
|
quicConfig.KeepAlivePeriod = net.QuicgoH3KeepAlivePeriod
|
||||||
|
} else if keepAlivePeriod > 0 {
|
||||||
|
quicConfig.KeepAlivePeriod = keepAlivePeriod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if quicParams.MaxIncomingStreams == 0 {
|
if quicParams.MaxIncomingStreams == 0 {
|
||||||
@@ -506,6 +508,8 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
var seq int64
|
var seq int64
|
||||||
var lastWrite time.Time
|
var lastWrite time.Time
|
||||||
|
|
||||||
|
dynamicHTTPClient := httpClient
|
||||||
|
dynamicXmuxClient := xmuxClient
|
||||||
for {
|
for {
|
||||||
// by offloading the uploads into a buffered pipe, multiple conn.Write
|
// by offloading the uploads into a buffered pipe, multiple conn.Write
|
||||||
// calls get automatically batched together into larger POST requests.
|
// calls get automatically batched together into larger POST requests.
|
||||||
@@ -540,13 +544,13 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
|
|
||||||
lastWrite = time.Now()
|
lastWrite = time.Now()
|
||||||
|
|
||||||
if xmuxClient != nil && (xmuxClient.LeftRequests.Add(-1) <= 0 ||
|
if dynamicXmuxClient != nil && (dynamicXmuxClient.LeftRequests.Add(-1) <= 0 ||
|
||||||
(xmuxClient.UnreusableAt != time.Time{} && lastWrite.After(xmuxClient.UnreusableAt))) {
|
(dynamicXmuxClient.UnreusableAt != time.Time{} && lastWrite.After(dynamicXmuxClient.UnreusableAt))) {
|
||||||
httpClient, xmuxClient = getHTTPClient(ctx, dest, streamSettings)
|
dynamicHTTPClient, dynamicXmuxClient = getHTTPClient(ctx, dest, streamSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func(hClient DialerClient) {
|
||||||
err := httpClient.PostPacket(
|
err := hClient.PostPacket(
|
||||||
ctx,
|
ctx,
|
||||||
requestURL.String(),
|
requestURL.String(),
|
||||||
sessionId,
|
sessionId,
|
||||||
@@ -559,9 +563,9 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
uploadPipeReader.Interrupt()
|
uploadPipeReader.Interrupt()
|
||||||
doSplit.Store(false)
|
doSplit.Store(false)
|
||||||
}
|
}
|
||||||
}()
|
}(dynamicHTTPClient)
|
||||||
|
|
||||||
if _, ok := httpClient.(*DefaultDialerClient); ok {
|
if _, ok := dynamicHTTPClient.(*DefaultDialerClient); ok {
|
||||||
<-wroteRequest.Wait()
|
<-wroteRequest.Wait()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user