Compare commits
52 Commits
vless-close
...
listable
| Author | SHA1 | Date | |
|---|---|---|---|
| 37efc4237a | |||
| 2c92339f95 | |||
| 9c46a2d55a | |||
| 19186edfa1 | |||
| f6a7e93923 | |||
| 077070dbe0 | |||
| c3af657c0e | |||
| 9a04eecaf9 | |||
| 1951a278ac | |||
| f1aee0b7c5 | |||
| 5173e5c15d | |||
| daf9cba29f | |||
| 445c0d456c | |||
| 0a42dba13e | |||
| cd8aab9544 | |||
| 59dc2cee2e | |||
| 5846f94784 | |||
| e0ee2350fc | |||
| 48164c8267 | |||
| 2d2102f654 | |||
| 262770564f | |||
| 777e31302c | |||
| 30d6a5221b | |||
| a778d3d273 | |||
| e813a3744f | |||
| 66628943d8 | |||
| 7ff06f65ec | |||
| 1cf5662949 | |||
| a6aca101d6 | |||
| 5836f36f69 | |||
| 6d6c045a5a | |||
| cfc78b3ac1 | |||
| 5f7474120f | |||
| 09f619d67c | |||
| 760223ad70 | |||
| d75b33a3a3 | |||
| 7c418486c8 | |||
| a384be0f84 | |||
| 649e989fa2 | |||
| 0443de7798 | |||
| 9a121a489b | |||
| 4bdf6e5c92 | |||
| ef2a967f12 | |||
| 92ada2dd1d | |||
| 8a9dbd407f | |||
| de6be7c5a9 | |||
| e742e84ded | |||
| 7726fbece0 | |||
| 0c09f4342b | |||
| 14e171ac8e | |||
| 07a0dafa41 | |||
| 0ca13452b8 |
@@ -95,11 +95,12 @@
|
||||
## GUI Clients
|
||||
|
||||
- OpenWrt
|
||||
- [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
|
||||
- [PassWall](https://github.com/Openwrt-Passwall/openwrt-passwall), [PassWall 2](https://github.com/Openwrt-Passwall/openwrt-passwall2)
|
||||
- [ShadowSocksR Plus+](https://github.com/fw876/helloworld)
|
||||
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
|
||||
- Asuswrt-Merlin
|
||||
- [XRAYUI](https://github.com/DanielLavrushin/asuswrt-merlin-xrayui)
|
||||
- [fancyss](https://github.com/hq450/fancyss)
|
||||
- Windows
|
||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||
- [Furious](https://github.com/LorenEteval/Furious)
|
||||
@@ -112,11 +113,11 @@
|
||||
- [SimpleXray](https://github.com/lhear/SimpleXray)
|
||||
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
||||
- iOS & macOS arm64 & tvOS
|
||||
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) ([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)
|
||||
- [OneXray](https://github.com/OneXray/OneXray)
|
||||
- macOS arm64 & x64
|
||||
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
|
||||
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973)
|
||||
- [V2rayU](https://github.com/yanue/V2rayU)
|
||||
- [V2RayXS](https://github.com/tzmax/V2RayXS)
|
||||
- [Furious](https://github.com/LorenEteval/Furious)
|
||||
@@ -137,6 +138,8 @@
|
||||
- iOS & macOS arm64 & tvOS
|
||||
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
||||
- [Loon](https://apps.apple.com/us/app/loon/id1373567447)
|
||||
- [Egern](https://apps.apple.com/us/app/egern/id1616105820)
|
||||
- [Quantumult X](https://apps.apple.com/us/app/quantumult-x/id1443988620)
|
||||
- Xray Tools
|
||||
- [xray-knife](https://github.com/lilendian0x00/xray-knife)
|
||||
- [xray-checker](https://github.com/kutovoys/xray-checker)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
If you found an issue related to security vulnerability or protocol-identification problem, please report it to us via "[Report a vulnerability](https://github.com/XTLS/Xray-core/security/advisories/new)" privately, instead of publish it publicly before we release the fixed version.
|
||||
|
||||
Thanks for your contribution to the FREE Internet!
|
||||
@@ -12,15 +12,12 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
router "github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/strmatcher"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// DNS is a DNS rely server.
|
||||
@@ -100,25 +97,6 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
|
||||
}
|
||||
|
||||
for _, ns := range config.NameServer {
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
||||
err := parseDomains(ns)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to parse dns domain rules: ").Base(err)
|
||||
}
|
||||
|
||||
expectedGeoip, err := router.GetGeoIPList(ns.ExpectedGeoip)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to parse dns expectIPs rules: ").Base(err)
|
||||
}
|
||||
ns.ExpectedGeoip = expectedGeoip
|
||||
|
||||
unexpectedGeoip, err := router.GetGeoIPList(ns.UnexpectedGeoip)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to parse dns unexpectedGeoip rules: ").Base(err)
|
||||
}
|
||||
ns.UnexpectedGeoip = unexpectedGeoip
|
||||
|
||||
}
|
||||
domainRuleCount += len(ns.PrioritizedDomain)
|
||||
}
|
||||
|
||||
@@ -602,76 +580,3 @@ func detectGUIPlatform() bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parseDomains(ns *NameServer) error {
|
||||
pureDomains := []*router.Domain{}
|
||||
|
||||
// convert to pure domain
|
||||
for _, pd := range ns.PrioritizedDomain {
|
||||
pureDomains = append(pureDomains, &router.Domain{
|
||||
Type: router.Domain_Type(pd.Type),
|
||||
Value: pd.Domain,
|
||||
})
|
||||
}
|
||||
|
||||
domainList := []*router.Domain{}
|
||||
for _, domain := range pureDomains {
|
||||
val := strings.Split(domain.Value, "_")
|
||||
if len(val) >= 2 {
|
||||
|
||||
fileName := val[0]
|
||||
code := val[1]
|
||||
|
||||
bs, err := filesystem.ReadAsset(fileName)
|
||||
if err != nil {
|
||||
return errors.New("failed to load file: ", fileName).Base(err)
|
||||
}
|
||||
bs = filesystem.Find(bs, []byte(code))
|
||||
var geosite router.GeoSite
|
||||
|
||||
if err := proto.Unmarshal(bs, &geosite); err != nil {
|
||||
return errors.New("failed Unmarshal :").Base(err)
|
||||
}
|
||||
|
||||
// parse attr
|
||||
if len(val) == 3 {
|
||||
siteWithAttr := strings.Split(val[2], ",")
|
||||
attrs := router.ParseAttrs(siteWithAttr)
|
||||
if !attrs.IsEmpty() {
|
||||
filteredDomains := make([]*router.Domain, 0, len(pureDomains))
|
||||
for _, domain := range geosite.Domain {
|
||||
if attrs.Match(domain) {
|
||||
filteredDomains = append(filteredDomains, domain)
|
||||
}
|
||||
}
|
||||
geosite.Domain = filteredDomains
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
domainList = append(domainList, geosite.Domain...)
|
||||
|
||||
// update ns.OriginalRules Size
|
||||
ruleTag := strings.Join(val, ":")
|
||||
for i, oRule := range ns.OriginalRules {
|
||||
if oRule.Rule == strings.ToLower(ruleTag) {
|
||||
ns.OriginalRules[i].Size = uint32(len(geosite.Domain))
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
domainList = append(domainList, domain)
|
||||
}
|
||||
}
|
||||
|
||||
// convert back to NameServer_PriorityDomain
|
||||
ns.PrioritizedDomain = []*NameServer_PriorityDomain{}
|
||||
for _, pd := range domainList {
|
||||
ns.PrioritizedDomain = append(ns.PrioritizedDomain, &NameServer_PriorityDomain{
|
||||
Type: ToDomainMatchingType(pd.Type),
|
||||
Domain: pd.Value,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+4
-4
@@ -541,7 +541,7 @@ func TestIPMatch(t *testing.T) {
|
||||
},
|
||||
ExpectedGeoip: []*router.GeoIP{
|
||||
{
|
||||
// local
|
||||
CountryCode: "local",
|
||||
Cidr: []*router.CIDR{
|
||||
{
|
||||
// inner ip, will not match
|
||||
@@ -565,7 +565,7 @@ func TestIPMatch(t *testing.T) {
|
||||
},
|
||||
ExpectedGeoip: []*router.GeoIP{
|
||||
{
|
||||
// test
|
||||
CountryCode: "test",
|
||||
Cidr: []*router.CIDR{
|
||||
{
|
||||
Ip: []byte{8, 8, 8, 8},
|
||||
@@ -574,7 +574,7 @@ func TestIPMatch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
// test
|
||||
CountryCode: "test",
|
||||
Cidr: []*router.CIDR{
|
||||
{
|
||||
Ip: []byte{8, 8, 8, 4},
|
||||
@@ -669,7 +669,7 @@ func TestLocalDomain(t *testing.T) {
|
||||
},
|
||||
ExpectedGeoip: []*router.GeoIP{
|
||||
{ // Will match localhost, localhost-a and localhost-b,
|
||||
// local
|
||||
CountryCode: "local",
|
||||
Cidr: []*router.CIDR{
|
||||
{Ip: []byte{127, 0, 0, 2}, Prefix: 32},
|
||||
{Ip: []byte{127, 0, 0, 3}, Prefix: 32},
|
||||
|
||||
+4
-1
@@ -2,6 +2,7 @@ package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -24,7 +25,9 @@ func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
|
||||
matchers: g,
|
||||
}
|
||||
|
||||
for _, mapping := range hosts {
|
||||
defer runtime.GC()
|
||||
for i, mapping := range hosts {
|
||||
hosts[i] = nil
|
||||
matcher, err := toStrMatcher(mapping.Type, mapping.Domain)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(context.Background(), err, "failed to create domain matcher, ignore domain rule [type: ", mapping.Type, ", domain: ", mapping.Domain, "]")
|
||||
|
||||
+9
-16
@@ -3,6 +3,7 @@ package dns
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -131,7 +132,8 @@ func NewClient(
|
||||
var rules []string
|
||||
ruleCurr := 0
|
||||
ruleIter := 0
|
||||
for _, domain := range ns.PrioritizedDomain {
|
||||
for i, domain := range ns.PrioritizedDomain {
|
||||
ns.PrioritizedDomain[i] = nil
|
||||
domainRule, err := toStrMatcher(domain.Type, domain.Domain)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to create domain matcher, ignore domain rule [type: ", domain.Type, ", domain: ", domain.Domain, "]")
|
||||
@@ -154,6 +156,8 @@ func NewClient(
|
||||
}
|
||||
updateDomainRule(domainRule, originalRuleIdx, *matcherInfos)
|
||||
}
|
||||
ns.PrioritizedDomain = nil
|
||||
runtime.GC()
|
||||
|
||||
// Establish expected IPs
|
||||
var expectedMatcher router.GeoIPMatcher
|
||||
@@ -162,6 +166,8 @@ func NewClient(
|
||||
if err != nil {
|
||||
return errors.New("failed to create expected ip matcher").Base(err).AtWarning()
|
||||
}
|
||||
ns.ExpectedGeoip = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
// Establish unexpected IPs
|
||||
@@ -171,6 +177,8 @@ func NewClient(
|
||||
if err != nil {
|
||||
return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning()
|
||||
}
|
||||
ns.UnexpectedGeoip = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
if len(clientIP) > 0 {
|
||||
@@ -297,18 +305,3 @@ func ResolveIpOptionOverride(queryStrategy QueryStrategy, ipOption dns.IPOption)
|
||||
return ipOption
|
||||
}
|
||||
}
|
||||
|
||||
func ToDomainMatchingType(t router.Domain_Type) DomainMatchingType {
|
||||
switch t {
|
||||
case router.Domain_Domain:
|
||||
return DomainMatchingType_Subdomain
|
||||
case router.Domain_Full:
|
||||
return DomainMatchingType_Full
|
||||
case router.Domain_Plain:
|
||||
return DomainMatchingType_Keyword
|
||||
case router.Domain_Regex:
|
||||
return DomainMatchingType_Regex
|
||||
default:
|
||||
panic("unknown domain type")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/apernet/quic-go"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
|
||||
@@ -160,7 +160,7 @@ func (s *ClassicNameServer) getCacheController() *CacheController {
|
||||
}
|
||||
|
||||
// sendQuery implements CachedNameserver.
|
||||
func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, fqdn string, option dns_feature.IPOption) {
|
||||
func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
||||
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
|
||||
|
||||
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||
@@ -171,7 +171,14 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, fqdn
|
||||
ctx: ctx,
|
||||
}
|
||||
s.addPendingRequest(udpReq)
|
||||
b, _ := dns.PackMessage(req.msg)
|
||||
b, err := dns.PackMessage(req.msg)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to pack dns query")
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
|
||||
b.UDP = ©Dest
|
||||
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
|
||||
|
||||
+83
-33
@@ -2,8 +2,9 @@ package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -20,14 +21,23 @@ type Instance struct {
|
||||
errorLogger log.Handler
|
||||
active bool
|
||||
dns bool
|
||||
mask4 int
|
||||
mask6 int
|
||||
}
|
||||
|
||||
// New creates a new log.Instance based on the given config.
|
||||
func New(ctx context.Context, config *Config) (*Instance, error) {
|
||||
m4, m6, err := ParseMaskAddress(config.MaskAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g := &Instance{
|
||||
config: config,
|
||||
active: false,
|
||||
dns: config.EnableDnsLog,
|
||||
mask4: m4,
|
||||
mask6: m6,
|
||||
}
|
||||
log.RegisterHandler(g)
|
||||
|
||||
@@ -104,7 +114,11 @@ func (g *Instance) Handle(msg log.Message) {
|
||||
|
||||
var Msg log.Message
|
||||
if g.config.MaskAddress != "" {
|
||||
Msg = &MaskedMsgWrapper{Message: msg, config: g.config}
|
||||
Msg = &MaskedMsgWrapper{
|
||||
Message: msg,
|
||||
Mask4: g.mask4,
|
||||
Mask6: g.mask6,
|
||||
}
|
||||
} else {
|
||||
Msg = msg
|
||||
}
|
||||
@@ -149,51 +163,87 @@ func (g *Instance) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseMaskAddress(c string) (int, int, error) {
|
||||
var m4, m6 int
|
||||
switch c {
|
||||
case "half":
|
||||
m4, m6 = 16, 32
|
||||
case "quarter":
|
||||
m4, m6 = 8, 16
|
||||
case "full":
|
||||
m4, m6 = 0, 0
|
||||
case "":
|
||||
// do nothing
|
||||
default:
|
||||
if parts := strings.Split(c, "+"); len(parts) > 0 {
|
||||
if len(parts) >= 1 && parts[0] != "" {
|
||||
i, err := strconv.Atoi(strings.TrimPrefix(parts[0], "/"))
|
||||
if err != nil {
|
||||
return 32, 128, err
|
||||
}
|
||||
m4 = i
|
||||
}
|
||||
if len(parts) >= 2 && parts[1] != "" {
|
||||
i, err := strconv.Atoi(strings.TrimPrefix(parts[1], "/"))
|
||||
if err != nil {
|
||||
return 32, 128, err
|
||||
}
|
||||
m6 = i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m4%8 != 0 || m4 > 32 || m4 < 0 {
|
||||
return 32, 128, errors.New("Log Mask: ipv4 mask must be divisible by 8 and between 0-32")
|
||||
}
|
||||
|
||||
return m4, m6, nil
|
||||
}
|
||||
|
||||
// MaskedMsgWrapper is to wrap the string() method to mask IP addresses in the log.
|
||||
type MaskedMsgWrapper struct {
|
||||
log.Message
|
||||
config *Config
|
||||
Mask4 int
|
||||
Mask6 int
|
||||
}
|
||||
|
||||
var (
|
||||
ipv4Regex = regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`)
|
||||
ipv6Regex = regexp.MustCompile(`(?:[\da-fA-F]{0,4}:[\da-fA-F]{0,4}){2,7}`)
|
||||
)
|
||||
|
||||
func (m *MaskedMsgWrapper) String() string {
|
||||
str := m.Message.String()
|
||||
|
||||
ipv4Regex := regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`)
|
||||
ipv6Regex := regexp.MustCompile(`((?:[\da-fA-F]{0,4}:[\da-fA-F]{0,4}){2,7})(?:[\/\\%](\d{1,3}))?`)
|
||||
|
||||
// Process ipv4
|
||||
maskedMsg := ipv4Regex.ReplaceAllStringFunc(str, func(ip string) string {
|
||||
parts := strings.Split(ip, ".")
|
||||
switch m.config.MaskAddress {
|
||||
case "half":
|
||||
return fmt.Sprintf("%s.%s.*.*", parts[0], parts[1])
|
||||
case "quarter":
|
||||
return fmt.Sprintf("%s.*.*.*", parts[0])
|
||||
case "full":
|
||||
return "[Masked IPv4]"
|
||||
default:
|
||||
return ip
|
||||
maskedMsg := ipv4Regex.ReplaceAllStringFunc(str, func(s string) string {
|
||||
if m.Mask4 == 32 {
|
||||
return s
|
||||
}
|
||||
if m.Mask4 == 0 {
|
||||
return "[Masked IPv4]"
|
||||
}
|
||||
|
||||
parts := strings.Split(s, ".")
|
||||
for i := m.Mask4 / 8; i < 4; i++ {
|
||||
parts[i] = "*"
|
||||
}
|
||||
return strings.Join(parts, ".")
|
||||
})
|
||||
|
||||
// process ipv6
|
||||
maskedMsg = ipv6Regex.ReplaceAllStringFunc(maskedMsg, func(ip string) string {
|
||||
parts := strings.Split(ip, ":")
|
||||
switch m.config.MaskAddress {
|
||||
case "half":
|
||||
if len(parts) >= 2 {
|
||||
return fmt.Sprintf("%s:%s::/32", parts[0], parts[1])
|
||||
}
|
||||
case "quarter":
|
||||
if len(parts) >= 1 {
|
||||
return fmt.Sprintf("%s::/16", parts[0])
|
||||
}
|
||||
case "full":
|
||||
return "Masked IPv6" // Do not use [Masked IPv6] like ipv4, or you will get "[[Masked IPv6]]" (v6 address already has [])
|
||||
default:
|
||||
return ip
|
||||
maskedMsg = ipv6Regex.ReplaceAllStringFunc(maskedMsg, func(s string) string {
|
||||
if m.Mask6 == 128 {
|
||||
return s
|
||||
}
|
||||
return ip
|
||||
if m.Mask6 == 0 {
|
||||
return "Masked IPv6"
|
||||
}
|
||||
ip := net.ParseIP(s)
|
||||
if ip == nil {
|
||||
return s
|
||||
}
|
||||
return ip.Mask(net.CIDRMask(m.Mask6, 128)).String() + "/" + strconv.Itoa(m.Mask6)
|
||||
})
|
||||
|
||||
return maskedMsg
|
||||
|
||||
@@ -2,6 +2,7 @@ package log_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
@@ -50,3 +51,39 @@ func TestCustomLogHandler(t *testing.T) {
|
||||
|
||||
common.Must(logger.Close())
|
||||
}
|
||||
|
||||
func TestMaskAddress(t *testing.T) {
|
||||
m4, m6, err := log.ParseMaskAddress("half")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
maskedAddr := log.MaskedMsgWrapper{
|
||||
Mask4: m4,
|
||||
Mask6: m6,
|
||||
}
|
||||
maskedAddr.Message = net.ParseIP("11.45.1.4")
|
||||
if maskedAddr.String() != "11.45.*.*" {
|
||||
t.Fatal("expected '11.45.*.*', but actually ", maskedAddr.String())
|
||||
}
|
||||
maskedAddr.Message = net.ParseIP("11:45:14:19:19:81:0::")
|
||||
if maskedAddr.String() != "11:45::/32" {
|
||||
t.Fatal("expected '11:45::/32', but actually", maskedAddr.String())
|
||||
}
|
||||
|
||||
m4, m6, err = log.ParseMaskAddress("/16+/64")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
maskedAddr = log.MaskedMsgWrapper{
|
||||
Mask4: m4,
|
||||
Mask6: m6,
|
||||
}
|
||||
maskedAddr.Message = net.ParseIP("11.45.1.4")
|
||||
if maskedAddr.String() != "11.45.*.*" {
|
||||
t.Fatal("expected '11.45.*.*', but actually ", maskedAddr.String())
|
||||
}
|
||||
maskedAddr.Message = net.ParseIP("11:45:14:19:19:81:0::")
|
||||
if maskedAddr.String() != "11:45:14:19::/64" {
|
||||
t.Fatal("expected '11:45:14:19::/64', but actually", maskedAddr.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/mux"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
"github.com/xtls/xray-core/features/stats"
|
||||
@@ -52,6 +53,20 @@ type AlwaysOnInboundHandler struct {
|
||||
}
|
||||
|
||||
func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) {
|
||||
// Set tag and sniffing config in context before creating proxy
|
||||
// This allows proxies like TUN to access these settings
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: tag})
|
||||
if receiverConfig.SniffingSettings != nil {
|
||||
ctx = session.ContextWithContent(ctx, &session.Content{
|
||||
SniffingRequest: session.SniffingRequest{
|
||||
Enabled: receiverConfig.SniffingSettings.Enabled,
|
||||
OverrideDestinationForProtocol: receiverConfig.SniffingSettings.DestinationOverride,
|
||||
ExcludeForDomain: receiverConfig.SniffingSettings.DomainsExcluded,
|
||||
MetadataOnly: receiverConfig.SniffingSettings.MetadataOnly,
|
||||
RouteOnly: receiverConfig.SniffingSettings.RouteOnly,
|
||||
},
|
||||
})
|
||||
}
|
||||
rawProxy, err := common.CreateObject(ctx, proxyConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -60,6 +60,7 @@ func (s *routingServer) AddRule(ctx context.Context, request *AddRuleRequest) (*
|
||||
return nil, errors.New("unsupported router implementation")
|
||||
|
||||
}
|
||||
|
||||
func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleRequest) (*RemoveRuleResponse, error) {
|
||||
if bo, ok := s.router.(routing.Router); ok {
|
||||
return &RemoveRuleResponse{}, bo.RemoveRule(request.RuleTag)
|
||||
@@ -67,6 +68,20 @@ func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleReque
|
||||
return nil, errors.New("unsupported router implementation")
|
||||
}
|
||||
|
||||
func (s *routingServer) ListRule(ctx context.Context, request *ListRuleRequest) (*ListRuleResponse, error) {
|
||||
if bo, ok := s.router.(routing.Router); ok {
|
||||
response := &ListRuleResponse{}
|
||||
for _, v := range bo.ListRule() {
|
||||
response.Rules = append(response.Rules, &ListRuleItem{
|
||||
Tag: v.GetOutboundTag(),
|
||||
RuleTag: v.GetRuleTag(),
|
||||
})
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
return nil, errors.New("unsupported router implementation")
|
||||
}
|
||||
|
||||
// NewRoutingServer creates a statistics service with statistics manager.
|
||||
func NewRoutingServer(router routing.Router, routingStats stats.Channel) RoutingServiceServer {
|
||||
return &routingServer{
|
||||
|
||||
+316
-273
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.35.1
|
||||
// protoc v5.28.2
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.2
|
||||
// source: app/router/command/command.proto
|
||||
|
||||
package command
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,25 +27,24 @@ const (
|
||||
// It conforms to the structure of xray.features.routing.Context and
|
||||
// xray.features.routing.Route.
|
||||
type RoutingContext struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
InboundTag string `protobuf:"bytes,1,opt,name=InboundTag,proto3" json:"InboundTag,omitempty"`
|
||||
Network net.Network `protobuf:"varint,2,opt,name=Network,proto3,enum=xray.common.net.Network" json:"Network,omitempty"`
|
||||
SourceIPs [][]byte `protobuf:"bytes,3,rep,name=SourceIPs,proto3" json:"SourceIPs,omitempty"`
|
||||
TargetIPs [][]byte `protobuf:"bytes,4,rep,name=TargetIPs,proto3" json:"TargetIPs,omitempty"`
|
||||
SourcePort uint32 `protobuf:"varint,5,opt,name=SourcePort,proto3" json:"SourcePort,omitempty"`
|
||||
TargetPort uint32 `protobuf:"varint,6,opt,name=TargetPort,proto3" json:"TargetPort,omitempty"`
|
||||
TargetDomain string `protobuf:"bytes,7,opt,name=TargetDomain,proto3" json:"TargetDomain,omitempty"`
|
||||
Protocol string `protobuf:"bytes,8,opt,name=Protocol,proto3" json:"Protocol,omitempty"`
|
||||
User string `protobuf:"bytes,9,opt,name=User,proto3" json:"User,omitempty"`
|
||||
Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,omitempty"`
|
||||
OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"`
|
||||
LocalIPs [][]byte `protobuf:"bytes,13,rep,name=LocalIPs,proto3" json:"LocalIPs,omitempty"`
|
||||
LocalPort uint32 `protobuf:"varint,14,opt,name=LocalPort,proto3" json:"LocalPort,omitempty"`
|
||||
VlessRoute uint32 `protobuf:"varint,15,opt,name=VlessRoute,proto3" json:"VlessRoute,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
InboundTag string `protobuf:"bytes,1,opt,name=InboundTag,proto3" json:"InboundTag,omitempty"`
|
||||
Network net.Network `protobuf:"varint,2,opt,name=Network,proto3,enum=xray.common.net.Network" json:"Network,omitempty"`
|
||||
SourceIPs [][]byte `protobuf:"bytes,3,rep,name=SourceIPs,proto3" json:"SourceIPs,omitempty"`
|
||||
TargetIPs [][]byte `protobuf:"bytes,4,rep,name=TargetIPs,proto3" json:"TargetIPs,omitempty"`
|
||||
SourcePort uint32 `protobuf:"varint,5,opt,name=SourcePort,proto3" json:"SourcePort,omitempty"`
|
||||
TargetPort uint32 `protobuf:"varint,6,opt,name=TargetPort,proto3" json:"TargetPort,omitempty"`
|
||||
TargetDomain string `protobuf:"bytes,7,opt,name=TargetDomain,proto3" json:"TargetDomain,omitempty"`
|
||||
Protocol string `protobuf:"bytes,8,opt,name=Protocol,proto3" json:"Protocol,omitempty"`
|
||||
User string `protobuf:"bytes,9,opt,name=User,proto3" json:"User,omitempty"`
|
||||
Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,omitempty"`
|
||||
OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"`
|
||||
LocalIPs [][]byte `protobuf:"bytes,13,rep,name=LocalIPs,proto3" json:"LocalIPs,omitempty"`
|
||||
LocalPort uint32 `protobuf:"varint,14,opt,name=LocalPort,proto3" json:"LocalPort,omitempty"`
|
||||
VlessRoute uint32 `protobuf:"varint,15,opt,name=VlessRoute,proto3" json:"VlessRoute,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RoutingContext) Reset() {
|
||||
@@ -201,11 +201,10 @@ func (x *RoutingContext) GetVlessRoute() uint32 {
|
||||
//
|
||||
// * If FieldSelectors is left empty, all fields will be returned.
|
||||
type SubscribeRoutingStatsRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
FieldSelectors []string `protobuf:"bytes,1,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
FieldSelectors []string `protobuf:"bytes,1,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SubscribeRoutingStatsRequest) Reset() {
|
||||
@@ -253,13 +252,12 @@ func (x *SubscribeRoutingStatsRequest) GetFieldSelectors() []string {
|
||||
// * PublishResult broadcasts the routing result to routing statistics channel
|
||||
// if set true.
|
||||
type TestRouteRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
RoutingContext *RoutingContext `protobuf:"bytes,1,opt,name=RoutingContext,proto3" json:"RoutingContext,omitempty"`
|
||||
FieldSelectors []string `protobuf:"bytes,2,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
|
||||
PublishResult bool `protobuf:"varint,3,opt,name=PublishResult,proto3" json:"PublishResult,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RoutingContext *RoutingContext `protobuf:"bytes,1,opt,name=RoutingContext,proto3" json:"RoutingContext,omitempty"`
|
||||
FieldSelectors []string `protobuf:"bytes,2,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
|
||||
PublishResult bool `protobuf:"varint,3,opt,name=PublishResult,proto3" json:"PublishResult,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TestRouteRequest) Reset() {
|
||||
@@ -314,11 +312,10 @@ func (x *TestRouteRequest) GetPublishResult() bool {
|
||||
}
|
||||
|
||||
type PrincipleTargetInfo struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"`
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PrincipleTargetInfo) Reset() {
|
||||
@@ -359,11 +356,10 @@ func (x *PrincipleTargetInfo) GetTag() []string {
|
||||
}
|
||||
|
||||
type OverrideInfo struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *OverrideInfo) Reset() {
|
||||
@@ -404,12 +400,11 @@ func (x *OverrideInfo) GetTarget() string {
|
||||
}
|
||||
|
||||
type BalancerMsg struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Override *OverrideInfo `protobuf:"bytes,5,opt,name=override,proto3" json:"override,omitempty"`
|
||||
PrincipleTarget *PrincipleTargetInfo `protobuf:"bytes,6,opt,name=principle_target,json=principleTarget,proto3" json:"principle_target,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Override *OverrideInfo `protobuf:"bytes,5,opt,name=override,proto3" json:"override,omitempty"`
|
||||
PrincipleTarget *PrincipleTargetInfo `protobuf:"bytes,6,opt,name=principle_target,json=principleTarget,proto3" json:"principle_target,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BalancerMsg) Reset() {
|
||||
@@ -457,11 +452,10 @@ func (x *BalancerMsg) GetPrincipleTarget() *PrincipleTargetInfo {
|
||||
}
|
||||
|
||||
type GetBalancerInfoRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetBalancerInfoRequest) Reset() {
|
||||
@@ -502,11 +496,10 @@ func (x *GetBalancerInfoRequest) GetTag() string {
|
||||
}
|
||||
|
||||
type GetBalancerInfoResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"`
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetBalancerInfoResponse) Reset() {
|
||||
@@ -547,12 +540,11 @@ func (x *GetBalancerInfoResponse) GetBalancer() *BalancerMsg {
|
||||
}
|
||||
|
||||
type OverrideBalancerTargetRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
BalancerTag string `protobuf:"bytes,1,opt,name=balancerTag,proto3" json:"balancerTag,omitempty"`
|
||||
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
BalancerTag string `protobuf:"bytes,1,opt,name=balancerTag,proto3" json:"balancerTag,omitempty"`
|
||||
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *OverrideBalancerTargetRequest) Reset() {
|
||||
@@ -600,9 +592,9 @@ func (x *OverrideBalancerTargetRequest) GetTarget() string {
|
||||
}
|
||||
|
||||
type OverrideBalancerTargetResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *OverrideBalancerTargetResponse) Reset() {
|
||||
@@ -636,12 +628,11 @@ func (*OverrideBalancerTargetResponse) Descriptor() ([]byte, []int) {
|
||||
}
|
||||
|
||||
type AddRuleRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Config *serial.TypedMessage `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
|
||||
ShouldAppend bool `protobuf:"varint,2,opt,name=shouldAppend,proto3" json:"shouldAppend,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Config *serial.TypedMessage `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
|
||||
ShouldAppend bool `protobuf:"varint,2,opt,name=shouldAppend,proto3" json:"shouldAppend,omitempty"`
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AddRuleRequest) Reset() {
|
||||
@@ -689,9 +680,9 @@ func (x *AddRuleRequest) GetShouldAppend() bool {
|
||||
}
|
||||
|
||||
type AddRuleResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AddRuleResponse) Reset() {
|
||||
@@ -725,11 +716,10 @@ func (*AddRuleResponse) Descriptor() ([]byte, []int) {
|
||||
}
|
||||
|
||||
type RemoveRuleRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RuleTag string `protobuf:"bytes,1,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
RuleTag string `protobuf:"bytes,1,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RemoveRuleRequest) Reset() {
|
||||
@@ -770,9 +760,9 @@ func (x *RemoveRuleRequest) GetRuleTag() string {
|
||||
}
|
||||
|
||||
type RemoveRuleResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RemoveRuleResponse) Reset() {
|
||||
@@ -805,15 +795,147 @@ func (*RemoveRuleResponse) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_command_command_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
type ListRuleRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ListRuleRequest) Reset() {
|
||||
*x = ListRuleRequest{}
|
||||
mi := &file_app_router_command_command_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ListRuleRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListRuleRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListRuleRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_command_command_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListRuleRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListRuleRequest) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_command_command_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
type ListRuleItem struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
RuleTag string `protobuf:"bytes,2,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ListRuleItem) Reset() {
|
||||
*x = ListRuleItem{}
|
||||
mi := &file_app_router_command_command_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ListRuleItem) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListRuleItem) ProtoMessage() {}
|
||||
|
||||
func (x *ListRuleItem) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_command_command_proto_msgTypes[15]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListRuleItem.ProtoReflect.Descriptor instead.
|
||||
func (*ListRuleItem) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_command_command_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *ListRuleItem) GetTag() string {
|
||||
if x != nil {
|
||||
return x.Tag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListRuleItem) GetRuleTag() string {
|
||||
if x != nil {
|
||||
return x.RuleTag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ListRuleResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Rules []*ListRuleItem `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ListRuleResponse) Reset() {
|
||||
*x = ListRuleResponse{}
|
||||
mi := &file_app_router_command_command_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ListRuleResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListRuleResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListRuleResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_command_command_proto_msgTypes[16]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListRuleResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListRuleResponse) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_command_command_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *ListRuleResponse) GetRules() []*ListRuleItem {
|
||||
if x != nil {
|
||||
return x.Rules
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_app_router_command_command_proto_msgTypes[14]
|
||||
mi := &file_app_router_command_command_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -825,7 +947,7 @@ func (x *Config) String() string {
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_command_command_proto_msgTypes[14]
|
||||
mi := &file_app_router_command_command_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -838,187 +960,103 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_command_command_proto_rawDescGZIP(), []int{14}
|
||||
return file_app_router_command_command_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
var File_app_router_command_command_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_app_router_command_command_proto_rawDesc = []byte{
|
||||
0x0a, 0x20, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x12, 0x17, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
|
||||
0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x18, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65,
|
||||
0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf6, 0x04, 0x0a, 0x0e, 0x52, 0x6f, 0x75,
|
||||
0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49,
|
||||
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e,
|
||||
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e,
|
||||
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12,
|
||||
0x1c, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x18, 0x03, 0x20, 0x03,
|
||||
0x28, 0x0c, 0x52, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a,
|
||||
0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c,
|
||||
0x52, 0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x53,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||
0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x54,
|
||||
0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||
0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x54,
|
||||
0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12,
|
||||
0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x55,
|
||||
0x73, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12,
|
||||
0x57, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
|
||||
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f,
|
||||
0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x41, 0x74, 0x74,
|
||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x41, 0x74,
|
||||
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x62,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x11, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f,
|
||||
0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4f, 0x75, 0x74,
|
||||
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61,
|
||||
0x6c, 0x49, 0x50, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x4c, 0x6f, 0x63, 0x61,
|
||||
0x6c, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72,
|
||||
0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f,
|
||||
0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75, 0x74, 0x65,
|
||||
0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75,
|
||||
0x74, 0x65, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
|
||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
|
||||
0x01, 0x22, 0x46, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x6f,
|
||||
0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
||||
0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64,
|
||||
0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x54, 0x65,
|
||||
0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f,
|
||||
0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
|
||||
0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52,
|
||||
0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12,
|
||||
0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
|
||||
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65,
|
||||
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69,
|
||||
0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d,
|
||||
0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x27, 0x0a,
|
||||
0x13, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x26, 0x0a, 0x0c, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69,
|
||||
0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xa9,
|
||||
0x01, 0x0a, 0x0b, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x41,
|
||||
0x0a, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
||||
0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72,
|
||||
0x69, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
|
||||
0x65, 0x12, 0x57, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x74,
|
||||
0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54,
|
||||
0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6e, 0x63,
|
||||
0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x2a, 0x0a, 0x16, 0x47, 0x65,
|
||||
0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x5b, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c,
|
||||
0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x40, 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
|
||||
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x42, 0x61,
|
||||
0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x52, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e,
|
||||
0x63, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x1d, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42,
|
||||
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72,
|
||||
0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, 0x6c, 0x61, 0x6e,
|
||||
0x63, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x20,
|
||||
0x0a, 0x1e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
|
||||
0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x6e, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0c,
|
||||
0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x0c, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64,
|
||||
0x22, 0x11, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c,
|
||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65,
|
||||
0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x54,
|
||||
0x61, 0x67, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x32, 0xbf, 0x05, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
|
||||
0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x35,
|
||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
|
||||
0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
|
||||
0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x00,
|
||||
0x30, 0x01, 0x12, 0x61, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12,
|
||||
0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
|
||||
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f,
|
||||
0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74,
|
||||
0x65, 0x78, 0x74, 0x22, 0x00, 0x12, 0x76, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61,
|
||||
0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
|
||||
0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e,
|
||||
0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8b, 0x01,
|
||||
0x0a, 0x16, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
|
||||
0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x36, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
|
||||
0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e,
|
||||
0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x37, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
||||
0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72,
|
||||
0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65,
|
||||
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x07, 0x41,
|
||||
0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
|
||||
0x2e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
|
||||
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c,
|
||||
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x0a, 0x52,
|
||||
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
|
||||
0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2f, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x61, 0x6e, 0x64, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52,
|
||||
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
const file_app_router_command_command_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
" app/router/command/command.proto\x12\x17xray.app.router.command\x1a\x18common/net/network.proto\x1a!common/serial/typed_message.proto\"\xf6\x04\n" +
|
||||
"\x0eRoutingContext\x12\x1e\n" +
|
||||
"\n" +
|
||||
"InboundTag\x18\x01 \x01(\tR\n" +
|
||||
"InboundTag\x122\n" +
|
||||
"\aNetwork\x18\x02 \x01(\x0e2\x18.xray.common.net.NetworkR\aNetwork\x12\x1c\n" +
|
||||
"\tSourceIPs\x18\x03 \x03(\fR\tSourceIPs\x12\x1c\n" +
|
||||
"\tTargetIPs\x18\x04 \x03(\fR\tTargetIPs\x12\x1e\n" +
|
||||
"\n" +
|
||||
"SourcePort\x18\x05 \x01(\rR\n" +
|
||||
"SourcePort\x12\x1e\n" +
|
||||
"\n" +
|
||||
"TargetPort\x18\x06 \x01(\rR\n" +
|
||||
"TargetPort\x12\"\n" +
|
||||
"\fTargetDomain\x18\a \x01(\tR\fTargetDomain\x12\x1a\n" +
|
||||
"\bProtocol\x18\b \x01(\tR\bProtocol\x12\x12\n" +
|
||||
"\x04User\x18\t \x01(\tR\x04User\x12W\n" +
|
||||
"\n" +
|
||||
"Attributes\x18\n" +
|
||||
" \x03(\v27.xray.app.router.command.RoutingContext.AttributesEntryR\n" +
|
||||
"Attributes\x12,\n" +
|
||||
"\x11OutboundGroupTags\x18\v \x03(\tR\x11OutboundGroupTags\x12 \n" +
|
||||
"\vOutboundTag\x18\f \x01(\tR\vOutboundTag\x12\x1a\n" +
|
||||
"\bLocalIPs\x18\r \x03(\fR\bLocalIPs\x12\x1c\n" +
|
||||
"\tLocalPort\x18\x0e \x01(\rR\tLocalPort\x12\x1e\n" +
|
||||
"\n" +
|
||||
"VlessRoute\x18\x0f \x01(\rR\n" +
|
||||
"VlessRoute\x1a=\n" +
|
||||
"\x0fAttributesEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"F\n" +
|
||||
"\x1cSubscribeRoutingStatsRequest\x12&\n" +
|
||||
"\x0eFieldSelectors\x18\x01 \x03(\tR\x0eFieldSelectors\"\xb1\x01\n" +
|
||||
"\x10TestRouteRequest\x12O\n" +
|
||||
"\x0eRoutingContext\x18\x01 \x01(\v2'.xray.app.router.command.RoutingContextR\x0eRoutingContext\x12&\n" +
|
||||
"\x0eFieldSelectors\x18\x02 \x03(\tR\x0eFieldSelectors\x12$\n" +
|
||||
"\rPublishResult\x18\x03 \x01(\bR\rPublishResult\"'\n" +
|
||||
"\x13PrincipleTargetInfo\x12\x10\n" +
|
||||
"\x03tag\x18\x01 \x03(\tR\x03tag\"&\n" +
|
||||
"\fOverrideInfo\x12\x16\n" +
|
||||
"\x06target\x18\x02 \x01(\tR\x06target\"\xa9\x01\n" +
|
||||
"\vBalancerMsg\x12A\n" +
|
||||
"\boverride\x18\x05 \x01(\v2%.xray.app.router.command.OverrideInfoR\boverride\x12W\n" +
|
||||
"\x10principle_target\x18\x06 \x01(\v2,.xray.app.router.command.PrincipleTargetInfoR\x0fprincipleTarget\"*\n" +
|
||||
"\x16GetBalancerInfoRequest\x12\x10\n" +
|
||||
"\x03tag\x18\x01 \x01(\tR\x03tag\"[\n" +
|
||||
"\x17GetBalancerInfoResponse\x12@\n" +
|
||||
"\bbalancer\x18\x01 \x01(\v2$.xray.app.router.command.BalancerMsgR\bbalancer\"Y\n" +
|
||||
"\x1dOverrideBalancerTargetRequest\x12 \n" +
|
||||
"\vbalancerTag\x18\x01 \x01(\tR\vbalancerTag\x12\x16\n" +
|
||||
"\x06target\x18\x02 \x01(\tR\x06target\" \n" +
|
||||
"\x1eOverrideBalancerTargetResponse\"n\n" +
|
||||
"\x0eAddRuleRequest\x128\n" +
|
||||
"\x06config\x18\x01 \x01(\v2 .xray.common.serial.TypedMessageR\x06config\x12\"\n" +
|
||||
"\fshouldAppend\x18\x02 \x01(\bR\fshouldAppend\"\x11\n" +
|
||||
"\x0fAddRuleResponse\"-\n" +
|
||||
"\x11RemoveRuleRequest\x12\x18\n" +
|
||||
"\aruleTag\x18\x01 \x01(\tR\aruleTag\"\x14\n" +
|
||||
"\x12RemoveRuleResponse\"\x11\n" +
|
||||
"\x0fListRuleRequest\":\n" +
|
||||
"\fListRuleItem\x12\x10\n" +
|
||||
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x18\n" +
|
||||
"\aruleTag\x18\x02 \x01(\tR\aruleTag\"O\n" +
|
||||
"\x10ListRuleResponse\x12;\n" +
|
||||
"\x05rules\x18\x01 \x03(\v2%.xray.app.router.command.ListRuleItemR\x05rules\"\b\n" +
|
||||
"\x06Config2\xa2\x06\n" +
|
||||
"\x0eRoutingService\x12{\n" +
|
||||
"\x15SubscribeRoutingStats\x125.xray.app.router.command.SubscribeRoutingStatsRequest\x1a'.xray.app.router.command.RoutingContext\"\x000\x01\x12a\n" +
|
||||
"\tTestRoute\x12).xray.app.router.command.TestRouteRequest\x1a'.xray.app.router.command.RoutingContext\"\x00\x12v\n" +
|
||||
"\x0fGetBalancerInfo\x12/.xray.app.router.command.GetBalancerInfoRequest\x1a0.xray.app.router.command.GetBalancerInfoResponse\"\x00\x12\x8b\x01\n" +
|
||||
"\x16OverrideBalancerTarget\x126.xray.app.router.command.OverrideBalancerTargetRequest\x1a7.xray.app.router.command.OverrideBalancerTargetResponse\"\x00\x12^\n" +
|
||||
"\aAddRule\x12'.xray.app.router.command.AddRuleRequest\x1a(.xray.app.router.command.AddRuleResponse\"\x00\x12g\n" +
|
||||
"\n" +
|
||||
"RemoveRule\x12*.xray.app.router.command.RemoveRuleRequest\x1a+.xray.app.router.command.RemoveRuleResponse\"\x00\x12a\n" +
|
||||
"\bListRule\x12(.xray.app.router.command.ListRuleRequest\x1a).xray.app.router.command.ListRuleResponse\"\x00Bg\n" +
|
||||
"\x1bcom.xray.app.router.commandP\x01Z,github.com/xtls/xray-core/app/router/command\xaa\x02\x17Xray.App.Router.Commandb\x06proto3"
|
||||
|
||||
var (
|
||||
file_app_router_command_command_proto_rawDescOnce sync.Once
|
||||
file_app_router_command_command_proto_rawDescData = file_app_router_command_command_proto_rawDesc
|
||||
file_app_router_command_command_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_app_router_command_command_proto_rawDescGZIP() []byte {
|
||||
file_app_router_command_command_proto_rawDescOnce.Do(func() {
|
||||
file_app_router_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_router_command_command_proto_rawDescData)
|
||||
file_app_router_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc)))
|
||||
})
|
||||
return file_app_router_command_command_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
|
||||
var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
|
||||
var file_app_router_command_command_proto_goTypes = []any{
|
||||
(*RoutingContext)(nil), // 0: xray.app.router.command.RoutingContext
|
||||
(*SubscribeRoutingStatsRequest)(nil), // 1: xray.app.router.command.SubscribeRoutingStatsRequest
|
||||
@@ -1034,36 +1072,42 @@ var file_app_router_command_command_proto_goTypes = []any{
|
||||
(*AddRuleResponse)(nil), // 11: xray.app.router.command.AddRuleResponse
|
||||
(*RemoveRuleRequest)(nil), // 12: xray.app.router.command.RemoveRuleRequest
|
||||
(*RemoveRuleResponse)(nil), // 13: xray.app.router.command.RemoveRuleResponse
|
||||
(*Config)(nil), // 14: xray.app.router.command.Config
|
||||
nil, // 15: xray.app.router.command.RoutingContext.AttributesEntry
|
||||
(net.Network)(0), // 16: xray.common.net.Network
|
||||
(*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage
|
||||
(*ListRuleRequest)(nil), // 14: xray.app.router.command.ListRuleRequest
|
||||
(*ListRuleItem)(nil), // 15: xray.app.router.command.ListRuleItem
|
||||
(*ListRuleResponse)(nil), // 16: xray.app.router.command.ListRuleResponse
|
||||
(*Config)(nil), // 17: xray.app.router.command.Config
|
||||
nil, // 18: xray.app.router.command.RoutingContext.AttributesEntry
|
||||
(net.Network)(0), // 19: xray.common.net.Network
|
||||
(*serial.TypedMessage)(nil), // 20: xray.common.serial.TypedMessage
|
||||
}
|
||||
var file_app_router_command_command_proto_depIdxs = []int32{
|
||||
16, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network
|
||||
15, // 1: xray.app.router.command.RoutingContext.Attributes:type_name -> xray.app.router.command.RoutingContext.AttributesEntry
|
||||
19, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network
|
||||
18, // 1: xray.app.router.command.RoutingContext.Attributes:type_name -> xray.app.router.command.RoutingContext.AttributesEntry
|
||||
0, // 2: xray.app.router.command.TestRouteRequest.RoutingContext:type_name -> xray.app.router.command.RoutingContext
|
||||
4, // 3: xray.app.router.command.BalancerMsg.override:type_name -> xray.app.router.command.OverrideInfo
|
||||
3, // 4: xray.app.router.command.BalancerMsg.principle_target:type_name -> xray.app.router.command.PrincipleTargetInfo
|
||||
5, // 5: xray.app.router.command.GetBalancerInfoResponse.balancer:type_name -> xray.app.router.command.BalancerMsg
|
||||
17, // 6: xray.app.router.command.AddRuleRequest.config:type_name -> xray.common.serial.TypedMessage
|
||||
1, // 7: xray.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> xray.app.router.command.SubscribeRoutingStatsRequest
|
||||
2, // 8: xray.app.router.command.RoutingService.TestRoute:input_type -> xray.app.router.command.TestRouteRequest
|
||||
6, // 9: xray.app.router.command.RoutingService.GetBalancerInfo:input_type -> xray.app.router.command.GetBalancerInfoRequest
|
||||
8, // 10: xray.app.router.command.RoutingService.OverrideBalancerTarget:input_type -> xray.app.router.command.OverrideBalancerTargetRequest
|
||||
10, // 11: xray.app.router.command.RoutingService.AddRule:input_type -> xray.app.router.command.AddRuleRequest
|
||||
12, // 12: xray.app.router.command.RoutingService.RemoveRule:input_type -> xray.app.router.command.RemoveRuleRequest
|
||||
0, // 13: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext
|
||||
0, // 14: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext
|
||||
7, // 15: xray.app.router.command.RoutingService.GetBalancerInfo:output_type -> xray.app.router.command.GetBalancerInfoResponse
|
||||
9, // 16: xray.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> xray.app.router.command.OverrideBalancerTargetResponse
|
||||
11, // 17: xray.app.router.command.RoutingService.AddRule:output_type -> xray.app.router.command.AddRuleResponse
|
||||
13, // 18: xray.app.router.command.RoutingService.RemoveRule:output_type -> xray.app.router.command.RemoveRuleResponse
|
||||
13, // [13:19] is the sub-list for method output_type
|
||||
7, // [7:13] is the sub-list for method input_type
|
||||
7, // [7:7] is the sub-list for extension type_name
|
||||
7, // [7:7] is the sub-list for extension extendee
|
||||
0, // [0:7] is the sub-list for field type_name
|
||||
20, // 6: xray.app.router.command.AddRuleRequest.config:type_name -> xray.common.serial.TypedMessage
|
||||
15, // 7: xray.app.router.command.ListRuleResponse.rules:type_name -> xray.app.router.command.ListRuleItem
|
||||
1, // 8: xray.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> xray.app.router.command.SubscribeRoutingStatsRequest
|
||||
2, // 9: xray.app.router.command.RoutingService.TestRoute:input_type -> xray.app.router.command.TestRouteRequest
|
||||
6, // 10: xray.app.router.command.RoutingService.GetBalancerInfo:input_type -> xray.app.router.command.GetBalancerInfoRequest
|
||||
8, // 11: xray.app.router.command.RoutingService.OverrideBalancerTarget:input_type -> xray.app.router.command.OverrideBalancerTargetRequest
|
||||
10, // 12: xray.app.router.command.RoutingService.AddRule:input_type -> xray.app.router.command.AddRuleRequest
|
||||
12, // 13: xray.app.router.command.RoutingService.RemoveRule:input_type -> xray.app.router.command.RemoveRuleRequest
|
||||
14, // 14: xray.app.router.command.RoutingService.ListRule:input_type -> xray.app.router.command.ListRuleRequest
|
||||
0, // 15: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext
|
||||
0, // 16: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext
|
||||
7, // 17: xray.app.router.command.RoutingService.GetBalancerInfo:output_type -> xray.app.router.command.GetBalancerInfoResponse
|
||||
9, // 18: xray.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> xray.app.router.command.OverrideBalancerTargetResponse
|
||||
11, // 19: xray.app.router.command.RoutingService.AddRule:output_type -> xray.app.router.command.AddRuleResponse
|
||||
13, // 20: xray.app.router.command.RoutingService.RemoveRule:output_type -> xray.app.router.command.RemoveRuleResponse
|
||||
16, // 21: xray.app.router.command.RoutingService.ListRule:output_type -> xray.app.router.command.ListRuleResponse
|
||||
15, // [15:22] is the sub-list for method output_type
|
||||
8, // [8:15] is the sub-list for method input_type
|
||||
8, // [8:8] is the sub-list for extension type_name
|
||||
8, // [8:8] is the sub-list for extension extendee
|
||||
0, // [0:8] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_app_router_command_command_proto_init() }
|
||||
@@ -1075,9 +1119,9 @@ func file_app_router_command_command_proto_init() {
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_app_router_command_command_proto_rawDesc,
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 16,
|
||||
NumMessages: 19,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
@@ -1086,7 +1130,6 @@ func file_app_router_command_command_proto_init() {
|
||||
MessageInfos: file_app_router_command_command_proto_msgTypes,
|
||||
}.Build()
|
||||
File_app_router_command_command_proto = out.File
|
||||
file_app_router_command_command_proto_rawDesc = nil
|
||||
file_app_router_command_command_proto_goTypes = nil
|
||||
file_app_router_command_command_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
@@ -104,6 +104,17 @@ message RemoveRuleRequest {
|
||||
|
||||
message RemoveRuleResponse {}
|
||||
|
||||
message ListRuleRequest {}
|
||||
|
||||
message ListRuleItem {
|
||||
string tag = 1;
|
||||
string ruleTag = 2;
|
||||
}
|
||||
|
||||
message ListRuleResponse{
|
||||
repeated ListRuleItem rules = 1;
|
||||
}
|
||||
|
||||
service RoutingService {
|
||||
rpc SubscribeRoutingStats(SubscribeRoutingStatsRequest)
|
||||
returns (stream RoutingContext) {}
|
||||
@@ -114,6 +125,8 @@ service RoutingService {
|
||||
|
||||
rpc AddRule(AddRuleRequest) returns (AddRuleResponse) {}
|
||||
rpc RemoveRule(RemoveRuleRequest) returns (RemoveRuleResponse) {}
|
||||
|
||||
rpc ListRule(ListRuleRequest) returns (ListRuleResponse) {}
|
||||
}
|
||||
|
||||
message Config {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.28.2
|
||||
// - protoc-gen-go-grpc v1.6.0
|
||||
// - protoc v6.33.2
|
||||
// source: app/router/command/command.proto
|
||||
|
||||
package command
|
||||
@@ -25,6 +25,7 @@ const (
|
||||
RoutingService_OverrideBalancerTarget_FullMethodName = "/xray.app.router.command.RoutingService/OverrideBalancerTarget"
|
||||
RoutingService_AddRule_FullMethodName = "/xray.app.router.command.RoutingService/AddRule"
|
||||
RoutingService_RemoveRule_FullMethodName = "/xray.app.router.command.RoutingService/RemoveRule"
|
||||
RoutingService_ListRule_FullMethodName = "/xray.app.router.command.RoutingService/ListRule"
|
||||
)
|
||||
|
||||
// RoutingServiceClient is the client API for RoutingService service.
|
||||
@@ -37,6 +38,7 @@ type RoutingServiceClient interface {
|
||||
OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error)
|
||||
AddRule(ctx context.Context, in *AddRuleRequest, opts ...grpc.CallOption) (*AddRuleResponse, error)
|
||||
RemoveRule(ctx context.Context, in *RemoveRuleRequest, opts ...grpc.CallOption) (*RemoveRuleResponse, error)
|
||||
ListRule(ctx context.Context, in *ListRuleRequest, opts ...grpc.CallOption) (*ListRuleResponse, error)
|
||||
}
|
||||
|
||||
type routingServiceClient struct {
|
||||
@@ -116,6 +118,16 @@ func (c *routingServiceClient) RemoveRule(ctx context.Context, in *RemoveRuleReq
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *routingServiceClient) ListRule(ctx context.Context, in *ListRuleRequest, opts ...grpc.CallOption) (*ListRuleResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListRuleResponse)
|
||||
err := c.cc.Invoke(ctx, RoutingService_ListRule_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// RoutingServiceServer is the server API for RoutingService service.
|
||||
// All implementations must embed UnimplementedRoutingServiceServer
|
||||
// for forward compatibility.
|
||||
@@ -126,6 +138,7 @@ type RoutingServiceServer interface {
|
||||
OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error)
|
||||
AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error)
|
||||
RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error)
|
||||
ListRule(context.Context, *ListRuleRequest) (*ListRuleResponse, error)
|
||||
mustEmbedUnimplementedRoutingServiceServer()
|
||||
}
|
||||
|
||||
@@ -137,22 +150,25 @@ type RoutingServiceServer interface {
|
||||
type UnimplementedRoutingServiceServer struct{}
|
||||
|
||||
func (UnimplementedRoutingServiceServer) SubscribeRoutingStats(*SubscribeRoutingStatsRequest, grpc.ServerStreamingServer[RoutingContext]) error {
|
||||
return status.Errorf(codes.Unimplemented, "method SubscribeRoutingStats not implemented")
|
||||
return status.Error(codes.Unimplemented, "method SubscribeRoutingStats not implemented")
|
||||
}
|
||||
func (UnimplementedRoutingServiceServer) TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method TestRoute not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method TestRoute not implemented")
|
||||
}
|
||||
func (UnimplementedRoutingServiceServer) GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetBalancerInfo not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method GetBalancerInfo not implemented")
|
||||
}
|
||||
func (UnimplementedRoutingServiceServer) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method OverrideBalancerTarget not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method OverrideBalancerTarget not implemented")
|
||||
}
|
||||
func (UnimplementedRoutingServiceServer) AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AddRule not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method AddRule not implemented")
|
||||
}
|
||||
func (UnimplementedRoutingServiceServer) RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RemoveRule not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method RemoveRule not implemented")
|
||||
}
|
||||
func (UnimplementedRoutingServiceServer) ListRule(context.Context, *ListRuleRequest) (*ListRuleResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListRule not implemented")
|
||||
}
|
||||
func (UnimplementedRoutingServiceServer) mustEmbedUnimplementedRoutingServiceServer() {}
|
||||
func (UnimplementedRoutingServiceServer) testEmbeddedByValue() {}
|
||||
@@ -165,7 +181,7 @@ type UnsafeRoutingServiceServer interface {
|
||||
}
|
||||
|
||||
func RegisterRoutingServiceServer(s grpc.ServiceRegistrar, srv RoutingServiceServer) {
|
||||
// If the following call pancis, it indicates UnimplementedRoutingServiceServer was
|
||||
// If the following call panics, it indicates UnimplementedRoutingServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
@@ -276,6 +292,24 @@ func _RoutingService_RemoveRule_Handler(srv interface{}, ctx context.Context, de
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RoutingService_ListRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListRuleRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RoutingServiceServer).ListRule(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RoutingService_ListRule_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RoutingServiceServer).ListRule(ctx, req.(*ListRuleRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -303,6 +337,10 @@ var RoutingService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "RemoveRule",
|
||||
Handler: _RoutingService_RemoveRule_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListRule",
|
||||
Handler: _RoutingService_ListRule_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
|
||||
+6
-44
@@ -57,7 +57,8 @@ type DomainMatcher struct {
|
||||
|
||||
func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
|
||||
g := strmatcher.NewMphMatcherGroup()
|
||||
for _, d := range domains {
|
||||
for i, d := range domains {
|
||||
domains[i] = nil
|
||||
matcherType, f := matcherTypeMap[d.Type]
|
||||
if !f {
|
||||
errors.LogError(context.Background(), "ignore unsupported domain type ", d.Type, " of rule ", d.Value)
|
||||
@@ -309,48 +310,6 @@ func (m *AttributeMatcher) Apply(ctx routing.Context) bool {
|
||||
return m.Match(attributes)
|
||||
}
|
||||
|
||||
// Geo attribute
|
||||
type GeoAttributeMatcher interface {
|
||||
Match(*Domain) bool
|
||||
}
|
||||
|
||||
type GeoBooleanMatcher string
|
||||
|
||||
func (m GeoBooleanMatcher) Match(domain *Domain) bool {
|
||||
for _, attr := range domain.Attribute {
|
||||
if attr.Key == string(m) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type GeoAttributeList struct {
|
||||
Matcher []GeoAttributeMatcher
|
||||
}
|
||||
|
||||
func (al *GeoAttributeList) Match(domain *Domain) bool {
|
||||
for _, matcher := range al.Matcher {
|
||||
if !matcher.Match(domain) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (al *GeoAttributeList) IsEmpty() bool {
|
||||
return len(al.Matcher) == 0
|
||||
}
|
||||
|
||||
func ParseAttrs(attrs []string) *GeoAttributeList {
|
||||
al := new(GeoAttributeList)
|
||||
for _, attr := range attrs {
|
||||
lc := strings.ToLower(attr)
|
||||
al.Matcher = append(al.Matcher, GeoBooleanMatcher(lc))
|
||||
}
|
||||
return al
|
||||
}
|
||||
|
||||
type ProcessNameMatcher struct {
|
||||
ProcessNames []string
|
||||
AbsPaths []string
|
||||
@@ -400,6 +359,9 @@ func NewProcessNameMatcher(names []string) *ProcessNameMatcher {
|
||||
}
|
||||
|
||||
func (m *ProcessNameMatcher) Apply(ctx routing.Context) bool {
|
||||
if len(ctx.GetSourceIPs()) == 0 {
|
||||
return false
|
||||
}
|
||||
srcPort := ctx.GetSourcePort().String()
|
||||
srcIP := ctx.GetSourceIPs()[0].String()
|
||||
var network string
|
||||
@@ -439,4 +401,4 @@ func (m *ProcessNameMatcher) Apply(ctx routing.Context) bool {
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,7 +822,8 @@ func (f *GeoIPSetFactory) Create(cidrGroups ...[]*CIDR) (*GeoIPSet, error) {
|
||||
var ipv4Builder, ipv6Builder netipx.IPSetBuilder
|
||||
|
||||
for _, cidrGroup := range cidrGroups {
|
||||
for _, cidrEntry := range cidrGroup {
|
||||
for i, cidrEntry := range cidrGroup {
|
||||
cidrGroup[i] = nil
|
||||
ipBytes := cidrEntry.GetIp()
|
||||
prefixLen := int(cidrEntry.GetPrefix())
|
||||
|
||||
|
||||
@@ -1,17 +1,40 @@
|
||||
package router_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func getAssetPath(file string) (string, error) {
|
||||
path := platform.GetAssetLocation(file)
|
||||
_, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
path := filepath.Join("..", "..", "resources", file)
|
||||
_, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("can't find %s in standard asset locations or {project_root}/resources", file)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't stat %s: %v", path, err)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't stat %s: %v", path, err)
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func TestGeoIPMatcher(t *testing.T) {
|
||||
cidrList := []*router.CIDR{
|
||||
{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
|
||||
@@ -159,11 +182,12 @@ func TestGeoIPReverseMatcher(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGeoIPMatcher4CN(t *testing.T) {
|
||||
geo := "geoip:cn"
|
||||
geoip, err := loadGeoIP(geo)
|
||||
ips, err := loadGeoIP("CN")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(geoip)
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
if matcher.Match([]byte{8, 8, 8, 8}) {
|
||||
@@ -172,11 +196,12 @@ func TestGeoIPMatcher4CN(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGeoIPMatcher6US(t *testing.T) {
|
||||
geo := "geoip:us"
|
||||
geoip, err := loadGeoIP(geo)
|
||||
ips, err := loadGeoIP("US")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(geoip)
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
|
||||
@@ -184,34 +209,37 @@ func TestGeoIPMatcher6US(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func loadGeoIP(geo string) (*router.GeoIP, error) {
|
||||
os.Setenv("XRAY_LOCATION_ASSET", filepath.Join("..", "..", "resources"))
|
||||
|
||||
geoip, err := conf.ToCidrList([]string{geo})
|
||||
func loadGeoIP(country string) ([]*router.CIDR, error) {
|
||||
path, err := getAssetPath("geoip.dat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
geoipBytes, err := filesystem.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
||||
geoip, err = router.GetGeoIPList(geoip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var geoipList router.GeoIPList
|
||||
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, geoip := range geoipList.Entry {
|
||||
if geoip.CountryCode == country {
|
||||
return geoip.Cidr, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(geoip) == 0 {
|
||||
panic("country not found: " + geo)
|
||||
}
|
||||
|
||||
return geoip[0], nil
|
||||
panic("country not found: " + country)
|
||||
}
|
||||
|
||||
func BenchmarkGeoIPMatcher4CN(b *testing.B) {
|
||||
geo := "geoip:cn"
|
||||
geoip, err := loadGeoIP(geo)
|
||||
ips, err := loadGeoIP("CN")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(geoip)
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
b.ResetTimer()
|
||||
@@ -222,11 +250,12 @@ func BenchmarkGeoIPMatcher4CN(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkGeoIPMatcher6US(b *testing.B) {
|
||||
geo := "geoip:us"
|
||||
geoip, err := loadGeoIP(geo)
|
||||
ips, err := loadGeoIP("US")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(geoip)
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
package router_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
. "github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/protocol/http"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
routing_session "github.com/xtls/xray-core/features/routing/session"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func withBackground() routing.Context {
|
||||
@@ -302,25 +300,32 @@ func TestRoutingRule(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func loadGeoSiteDomains(geo string) ([]*Domain, error) {
|
||||
os.Setenv("XRAY_LOCATION_ASSET", filepath.Join("..", "..", "resources"))
|
||||
|
||||
domains, err := conf.ParseDomainRule(geo)
|
||||
func loadGeoSite(country string) ([]*Domain, error) {
|
||||
path, err := getAssetPath("geosite.dat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
geositeBytes, err := filesystem.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
||||
domains, err = router.GetDomainList(domains)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var geositeList GeoSiteList
|
||||
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, site := range geositeList.Entry {
|
||||
if site.CountryCode == country {
|
||||
return site.Domain, nil
|
||||
}
|
||||
}
|
||||
return domains, nil
|
||||
|
||||
return nil, errors.New("country not found: " + country)
|
||||
}
|
||||
|
||||
func TestChinaSites(t *testing.T) {
|
||||
domains, err := loadGeoSiteDomains("geosite:cn")
|
||||
domains, err := loadGeoSite("CN")
|
||||
common.Must(err)
|
||||
|
||||
acMatcher, err := NewMphMatcherGroup(domains)
|
||||
@@ -361,50 +366,8 @@ func TestChinaSites(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestChinaSitesWithAttrs(t *testing.T) {
|
||||
domains, err := loadGeoSiteDomains("geosite:google@cn")
|
||||
common.Must(err)
|
||||
|
||||
acMatcher, err := NewMphMatcherGroup(domains)
|
||||
common.Must(err)
|
||||
|
||||
type TestCase struct {
|
||||
Domain string
|
||||
Output bool
|
||||
}
|
||||
testCases := []TestCase{
|
||||
{
|
||||
Domain: "google.cn",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Domain: "recaptcha.net",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Domain: "164.com",
|
||||
Output: false,
|
||||
},
|
||||
{
|
||||
Domain: "164.com",
|
||||
Output: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i := 0; i < 1024; i++ {
|
||||
testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
r := acMatcher.ApplyDomain(testCase.Domain)
|
||||
if r != testCase.Output {
|
||||
t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMphDomainMatcher(b *testing.B) {
|
||||
domains, err := loadGeoSiteDomains("geosite:cn")
|
||||
domains, err := loadGeoSite("CN")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := NewMphMatcherGroup(domains)
|
||||
@@ -449,11 +412,11 @@ func BenchmarkMultiGeoIPMatcher(b *testing.B) {
|
||||
var geoips []*GeoIP
|
||||
|
||||
{
|
||||
ips, err := loadGeoIP("geoip:cn")
|
||||
ips, err := loadGeoIP("CN")
|
||||
common.Must(err)
|
||||
geoips = append(geoips, &GeoIP{
|
||||
CountryCode: "CN",
|
||||
Cidr: ips.Cidr,
|
||||
Cidr: ips,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -462,25 +425,25 @@ func BenchmarkMultiGeoIPMatcher(b *testing.B) {
|
||||
common.Must(err)
|
||||
geoips = append(geoips, &GeoIP{
|
||||
CountryCode: "JP",
|
||||
Cidr: ips.Cidr,
|
||||
Cidr: ips,
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
ips, err := loadGeoIP("geoip:ca")
|
||||
ips, err := loadGeoIP("CA")
|
||||
common.Must(err)
|
||||
geoips = append(geoips, &GeoIP{
|
||||
CountryCode: "CA",
|
||||
Cidr: ips.Cidr,
|
||||
Cidr: ips,
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
ips, err := loadGeoIP("geoip:us")
|
||||
ips, err := loadGeoIP("US")
|
||||
common.Must(err)
|
||||
geoips = append(geoips, &GeoIP{
|
||||
CountryCode: "US",
|
||||
Cidr: ips.Cidr,
|
||||
Cidr: ips,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+11
-99
@@ -7,10 +7,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type Rule struct {
|
||||
@@ -76,19 +74,13 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||
}
|
||||
|
||||
if len(rr.Geoip) > 0 {
|
||||
geoip := rr.Geoip
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
||||
var err error
|
||||
geoip, err = GetGeoIPList(rr.Geoip)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build geoip from mmap").Base(err)
|
||||
}
|
||||
}
|
||||
cond, err := NewIPMatcher(geoip, MatcherAsType_Target)
|
||||
cond, err := NewIPMatcher(rr.Geoip, MatcherAsType_Target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
rr.Geoip = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
if len(rr.SourceGeoip) > 0 {
|
||||
@@ -97,6 +89,8 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
rr.SourceGeoip = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
if len(rr.LocalGeoip) > 0 {
|
||||
@@ -106,24 +100,19 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||
}
|
||||
conds.Add(cond)
|
||||
errors.LogWarning(context.Background(), "Due to some limitations, in UDP connections, localIP is always equal to listen interface IP, so \"localIP\" rule condition does not work properly on UDP inbound connections that listen on all interfaces")
|
||||
rr.LocalGeoip = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
if len(rr.Domain) > 0 {
|
||||
domains := rr.Domain
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
||||
var err error
|
||||
domains, err = GetDomainList(rr.Domain)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build domains from mmap").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
matcher, err := NewMphMatcherGroup(domains)
|
||||
matcher, err := NewMphMatcherGroup(rr.Domain)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err)
|
||||
}
|
||||
errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(domains), " domain rule(s)")
|
||||
errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)")
|
||||
conds.Add(matcher)
|
||||
rr.Domain = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
if len(rr.Process) > 0 {
|
||||
@@ -183,80 +172,3 @@ func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatch
|
||||
return nil, errors.New("unrecognized balancer type")
|
||||
}
|
||||
}
|
||||
|
||||
func GetGeoIPList(ips []*GeoIP) ([]*GeoIP, error) {
|
||||
geoipList := []*GeoIP{}
|
||||
for _, ip := range ips {
|
||||
if ip.CountryCode != "" {
|
||||
val := strings.Split(ip.CountryCode, "_")
|
||||
fileName := "geoip.dat"
|
||||
if len(val) == 2 {
|
||||
fileName = strings.ToLower(val[0])
|
||||
}
|
||||
bs, err := filesystem.ReadAsset(fileName)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to load file: ", fileName).Base(err)
|
||||
}
|
||||
bs = filesystem.Find(bs, []byte(ip.CountryCode))
|
||||
|
||||
var geoip GeoIP
|
||||
|
||||
if err := proto.Unmarshal(bs, &geoip); err != nil {
|
||||
return nil, errors.New("failed Unmarshal :").Base(err)
|
||||
}
|
||||
geoipList = append(geoipList, &geoip)
|
||||
|
||||
} else {
|
||||
geoipList = append(geoipList, ip)
|
||||
}
|
||||
}
|
||||
return geoipList, nil
|
||||
|
||||
}
|
||||
|
||||
func GetDomainList(domains []*Domain) ([]*Domain, error) {
|
||||
domainList := []*Domain{}
|
||||
for _, domain := range domains {
|
||||
val := strings.Split(domain.Value, "_")
|
||||
|
||||
if len(val) >= 2 {
|
||||
|
||||
fileName := val[0]
|
||||
code := val[1]
|
||||
|
||||
bs, err := filesystem.ReadAsset(fileName)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to load file: ", fileName).Base(err)
|
||||
}
|
||||
bs = filesystem.Find(bs, []byte(code))
|
||||
var geosite GeoSite
|
||||
|
||||
if err := proto.Unmarshal(bs, &geosite); err != nil {
|
||||
return nil, errors.New("failed Unmarshal :").Base(err)
|
||||
}
|
||||
|
||||
// parse attr
|
||||
if len(val) == 3 {
|
||||
siteWithAttr := strings.Split(val[2], ",")
|
||||
attrs := ParseAttrs(siteWithAttr)
|
||||
|
||||
if !attrs.IsEmpty() {
|
||||
filteredDomains := make([]*Domain, 0, len(domains))
|
||||
for _, domain := range geosite.Domain {
|
||||
if attrs.Match(domain) {
|
||||
filteredDomains = append(filteredDomains, domain)
|
||||
}
|
||||
}
|
||||
geosite.Domain = filteredDomains
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
domainList = append(domainList, geosite.Domain...)
|
||||
|
||||
} else {
|
||||
domainList = append(domainList, domain)
|
||||
}
|
||||
}
|
||||
return domainList, nil
|
||||
}
|
||||
|
||||
+16
-1
@@ -2,7 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
sync "sync"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -181,6 +181,21 @@ func (r *Router) RemoveRule(tag string) error {
|
||||
return errors.New("empty tag name!")
|
||||
|
||||
}
|
||||
|
||||
// ListRule implements routing.Router
|
||||
func (r *Router) ListRule() []routing.Route {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
ruleList := make([]routing.Route, 0)
|
||||
for _, rule := range r.rules {
|
||||
ruleList = append(ruleList, &Route{
|
||||
outboundTag: rule.Tag,
|
||||
ruleTag: rule.RuleTag,
|
||||
})
|
||||
}
|
||||
return ruleList
|
||||
}
|
||||
|
||||
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
|
||||
// SkipDNSResolve is set from DNS module.
|
||||
// the DOH remote server maybe a domain name,
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package antireplay
|
||||
|
||||
type GeneralizedReplayFilter interface {
|
||||
Interval() int64
|
||||
Check(sum []byte) bool
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package antireplay
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkMapFilter(b *testing.B) {
|
||||
filter := NewMapFilter[[16]byte](120)
|
||||
var sample [16]byte
|
||||
reader := bufio.NewReader(rand.Reader)
|
||||
reader.Read(sample[:])
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
reader.Read(sample[:])
|
||||
filter.Check(sample)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapFilter(t *testing.T) {
|
||||
filter := NewMapFilter[[16]byte](120)
|
||||
var sample [16]byte
|
||||
rand.Read(sample[:])
|
||||
filter.Check(sample)
|
||||
if filter.Check(sample) {
|
||||
t.Error("Unexpected true negative")
|
||||
}
|
||||
sample[0]++
|
||||
if !filter.Check(sample) {
|
||||
t.Error("Unexpected false positive")
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package antireplay
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
ss_bloomring "github.com/v2fly/ss-bloomring"
|
||||
)
|
||||
|
||||
type BloomRing struct {
|
||||
*ss_bloomring.BloomRing
|
||||
lock *sync.Mutex
|
||||
}
|
||||
|
||||
func (b BloomRing) Interval() int64 {
|
||||
return 9999999
|
||||
}
|
||||
|
||||
func (b BloomRing) Check(sum []byte) bool {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if b.Test(sum) {
|
||||
return false
|
||||
}
|
||||
b.Add(sum)
|
||||
return true
|
||||
}
|
||||
|
||||
func NewBloomRing() BloomRing {
|
||||
const (
|
||||
DefaultSFCapacity = 1e6
|
||||
// FalsePositiveRate
|
||||
DefaultSFFPR = 1e-6
|
||||
DefaultSFSlot = 10
|
||||
)
|
||||
return BloomRing{ss_bloomring.NewBloomRing(DefaultSFSlot, DefaultSFCapacity, DefaultSFFPR), &sync.Mutex{}}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package antireplay
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ReplayFilter checks for replay attacks.
|
||||
type ReplayFilter[T comparable] struct {
|
||||
lock sync.Mutex
|
||||
poolA map[T]struct{}
|
||||
poolB map[T]struct{}
|
||||
interval time.Duration
|
||||
lastClean time.Time
|
||||
}
|
||||
|
||||
// NewMapFilter create a new filter with specifying the expiration time interval in seconds.
|
||||
func NewMapFilter[T comparable](interval int64) *ReplayFilter[T] {
|
||||
filter := &ReplayFilter[T]{
|
||||
poolA: make(map[T]struct{}),
|
||||
poolB: make(map[T]struct{}),
|
||||
interval: time.Duration(interval) * time.Second,
|
||||
lastClean: time.Now(),
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
// Check determines if there are duplicate records.
|
||||
func (filter *ReplayFilter[T]) Check(sum T) bool {
|
||||
filter.lock.Lock()
|
||||
defer filter.lock.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
if now.Sub(filter.lastClean) >= filter.interval {
|
||||
filter.poolB = filter.poolA
|
||||
filter.poolA = make(map[T]struct{})
|
||||
filter.lastClean = now
|
||||
}
|
||||
|
||||
_, existsA := filter.poolA[sum]
|
||||
_, existsB := filter.poolB[sum]
|
||||
if !existsA && !existsB {
|
||||
filter.poolA[sum] = struct{}{}
|
||||
}
|
||||
return !(existsA || existsB)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package antireplay
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
cuckoo "github.com/seiflotfy/cuckoofilter"
|
||||
)
|
||||
|
||||
const replayFilterCapacity = 100000
|
||||
|
||||
// ReplayFilter checks for replay attacks.
|
||||
type ReplayFilter struct {
|
||||
lock sync.Mutex
|
||||
poolA *cuckoo.Filter
|
||||
poolB *cuckoo.Filter
|
||||
poolSwap bool
|
||||
lastSwap int64
|
||||
interval int64
|
||||
}
|
||||
|
||||
// NewReplayFilter create a new filter with specifying the expiration time interval in seconds.
|
||||
func NewReplayFilter(interval int64) *ReplayFilter {
|
||||
filter := &ReplayFilter{}
|
||||
filter.interval = interval
|
||||
return filter
|
||||
}
|
||||
|
||||
// Interval in second for expiration time for duplicate records.
|
||||
func (filter *ReplayFilter) Interval() int64 {
|
||||
return filter.interval
|
||||
}
|
||||
|
||||
// Check determines if there are duplicate records.
|
||||
func (filter *ReplayFilter) Check(sum []byte) bool {
|
||||
filter.lock.Lock()
|
||||
defer filter.lock.Unlock()
|
||||
|
||||
now := time.Now().Unix()
|
||||
if filter.lastSwap == 0 {
|
||||
filter.lastSwap = now
|
||||
filter.poolA = cuckoo.NewFilter(replayFilterCapacity)
|
||||
filter.poolB = cuckoo.NewFilter(replayFilterCapacity)
|
||||
}
|
||||
|
||||
elapsed := now - filter.lastSwap
|
||||
if elapsed >= filter.Interval() {
|
||||
if filter.poolSwap {
|
||||
filter.poolA.Reset()
|
||||
} else {
|
||||
filter.poolB.Reset()
|
||||
}
|
||||
filter.poolSwap = !filter.poolSwap
|
||||
filter.lastSwap = now
|
||||
}
|
||||
|
||||
return filter.poolA.InsertUnique(sum) && filter.poolB.InsertUnique(sum)
|
||||
}
|
||||
@@ -4,22 +4,19 @@ import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// PrintMigrateFeatureInfo prints a notice of the upcoming feature migration.
|
||||
// Place it after the source feature related config file pharser code.
|
||||
// Important note: Only use this when the target migrating feature is under construction.
|
||||
// Important note: Even when the target migrating feature has finished its construction, this notice can still be used yet before announcing deprecation of the old feature.
|
||||
// PrintNonRemovalDeprecatedFeatureWarning prints a warning of the deprecated feature that won't be removed in the near future.
|
||||
// Do not remove this function even there is no reference to it.
|
||||
func PrintMigrateFeatureInfo(sourceFeature string, targetFeature string) {
|
||||
LogInfo(context.Background(), "The feature "+sourceFeature+" will be migrated to "+targetFeature+" in the future.")
|
||||
func PrintNonRemovalDeprecatedFeatureWarning(sourceFeature string, targetFeature string) {
|
||||
LogWarning(context.Background(), "The feature "+sourceFeature+" is deprecated, not recommended for using and might be removed. Please migrate to "+targetFeature+" as soon as possible.")
|
||||
}
|
||||
|
||||
// PrintDeprecatedFeatureWarning prints a warning for deprecated and going to be removed feature.
|
||||
// Do not remove this function even there is no reference to it.
|
||||
func PrintDeprecatedFeatureWarning(feature string, migrateFeature string) {
|
||||
if len(migrateFeature) > 0 {
|
||||
LogWarning(context.Background(), "This feature "+feature+" is deprecated and being migrated to "+migrateFeature+". Please update your config(s) according to release note and documentation before removal.")
|
||||
LogWarning(context.Background(), "This feature "+feature+" is deprecated, will be removed soon and being migrated to "+migrateFeature+". Please update your config(s) according to release note and documentation before removal.")
|
||||
} else {
|
||||
LogWarning(context.Background(), "This feature "+feature+" is deprecated. Please update your config(s) according to release note and documentation before removal.")
|
||||
LogWarning(context.Background(), "This feature "+feature+" is deprecated and will be removed soon. Please update your config(s) according to release note and documentation before removal.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,16 @@ func PortListFromProto(l *PortList) MemoryPortList {
|
||||
return mpl
|
||||
}
|
||||
|
||||
func (l *PortList) Ports() []uint32 {
|
||||
var ports []uint32
|
||||
for _, r := range l.Range {
|
||||
for i := uint32(r.From); i <= uint32(r.To); i++ {
|
||||
ports = append(ports, i)
|
||||
}
|
||||
}
|
||||
return ports
|
||||
}
|
||||
|
||||
func (mpl MemoryPortList) Contains(port Port) bool {
|
||||
for _, pr := range mpl {
|
||||
if pr.Contains(port) {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package filesystem
|
||||
|
||||
func DecodeVarint(buf []byte) (x uint64, n int) {
|
||||
for shift := uint(0); shift < 64; shift += 7 {
|
||||
if n >= len(buf) {
|
||||
return 0, 0
|
||||
}
|
||||
b := uint64(buf[n])
|
||||
n++
|
||||
x |= (b & 0x7F) << shift
|
||||
if (b & 0x80) == 0 {
|
||||
return x, n
|
||||
}
|
||||
}
|
||||
|
||||
// The number is too large to represent in a 64-bit value.
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func Find(data, code []byte) []byte {
|
||||
codeL := len(code)
|
||||
if codeL == 0 {
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
dataL := len(data)
|
||||
if dataL < 2 {
|
||||
return nil
|
||||
}
|
||||
x, y := DecodeVarint(data[1:])
|
||||
if x == 0 && y == 0 {
|
||||
return nil
|
||||
}
|
||||
headL, bodyL := 1+y, int(x)
|
||||
dataL -= headL
|
||||
if dataL < bodyL {
|
||||
return nil
|
||||
}
|
||||
data = data[headL:]
|
||||
if int(data[1]) == codeL {
|
||||
for i := 0; i < codeL && data[2+i] == code[i]; i++ {
|
||||
if i+1 == codeL {
|
||||
return data[:bodyL]
|
||||
}
|
||||
}
|
||||
}
|
||||
if dataL == bodyL {
|
||||
return nil
|
||||
}
|
||||
data = data[bodyL:]
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
//go:build !windows && !wasm
|
||||
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
@@ -19,29 +16,6 @@ var NewFileReader FileReaderFunc = func(path string) (io.ReadCloser, error) {
|
||||
}
|
||||
|
||||
func ReadFile(path string) ([]byte, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
size := stat.Size()
|
||||
if size == 0 {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
// use mmap to save RAM
|
||||
bs, err := syscall.Mmap(int(file.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
|
||||
if err == nil {
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
// fallback
|
||||
reader, err := NewFileReader(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -55,6 +29,10 @@ func ReadAsset(file string) ([]byte, error) {
|
||||
return ReadFile(platform.GetAssetLocation(file))
|
||||
}
|
||||
|
||||
func OpenAsset(file string) (io.ReadCloser, error) {
|
||||
return NewFileReader(platform.GetAssetLocation(file))
|
||||
}
|
||||
|
||||
func ReadCert(file string) ([]byte, error) {
|
||||
if filepath.IsAbs(file) {
|
||||
return ReadFile(file)
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
//go:build windows || wasm
|
||||
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
)
|
||||
|
||||
type FileReaderFunc func(path string) (io.ReadCloser, error)
|
||||
|
||||
var NewFileReader FileReaderFunc = func(path string) (io.ReadCloser, error) {
|
||||
return os.Open(path)
|
||||
}
|
||||
|
||||
func ReadFile(path string) ([]byte, error) {
|
||||
reader, err := NewFileReader(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
return buf.ReadAllToBytes(reader)
|
||||
}
|
||||
|
||||
func ReadAsset(file string) ([]byte, error) {
|
||||
return ReadFile(platform.GetAssetLocation(file))
|
||||
}
|
||||
|
||||
func ReadCert(file string) ([]byte, error) {
|
||||
if filepath.IsAbs(file) {
|
||||
return ReadFile(file)
|
||||
}
|
||||
return ReadFile(platform.GetCertLocation(file))
|
||||
}
|
||||
|
||||
func CopyFile(dst string, src string) error {
|
||||
bytes, err := ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Write(bytes)
|
||||
return err
|
||||
}
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
import "path/filepath"
|
||||
|
||||
func LineSeparator() string {
|
||||
return "\r\n"
|
||||
@@ -14,7 +12,6 @@ func LineSeparator() string {
|
||||
// GetAssetLocation searches for `file` in the env dir and the executable dir
|
||||
func GetAssetLocation(file string) string {
|
||||
assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir)
|
||||
|
||||
return filepath.Join(assetPath, file)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/quic-go/quic-go/quicvarint"
|
||||
"github.com/apernet/quic-go/quicvarint"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
@@ -87,10 +88,10 @@ func Organization(org string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func MustGenerate(parent *Certificate, opts ...Option) *Certificate {
|
||||
func MustGenerate(parent *Certificate, opts ...Option) (*Certificate, [32]byte) {
|
||||
cert, err := Generate(parent, opts...)
|
||||
common.Must(err)
|
||||
return cert
|
||||
return cert, sha256.Sum256(cert.Certificate)
|
||||
}
|
||||
|
||||
func publicKey(priv interface{}) interface{} {
|
||||
|
||||
@@ -2,10 +2,12 @@ package singbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
B "github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
@@ -29,6 +31,8 @@ type PacketConnWrapper struct {
|
||||
cached buf.MultiBuffer
|
||||
}
|
||||
|
||||
// This ReadPacket implemented a timeout to avoid goroutine leak like PipeConnWrapper.Read()
|
||||
// as a temporarily solution
|
||||
func (w *PacketConnWrapper) ReadPacket(buffer *B.Buffer) (M.Socksaddr, error) {
|
||||
if w.cached != nil {
|
||||
mb, bb := buf.SplitFirst(w.cached)
|
||||
@@ -47,10 +51,30 @@ func (w *PacketConnWrapper) ReadPacket(buffer *B.Buffer) (M.Socksaddr, error) {
|
||||
return ToSocksaddr(destination), nil
|
||||
}
|
||||
}
|
||||
mb, err := w.ReadMultiBuffer()
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, err
|
||||
|
||||
// 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)
|
||||
if bb == nil {
|
||||
return M.Socksaddr{}, nil
|
||||
|
||||
+2
-1
@@ -35,6 +35,7 @@ func init() {
|
||||
if strings.ToLower(platform.NewEnvFlag(platform.XUDPLog).GetValue(func() string { return "" })) == "true" {
|
||||
Show = true
|
||||
}
|
||||
BaseKey = make([]byte, 32)
|
||||
rand.Read(BaseKey)
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // this is not nice, but need to give some time for Android to setup ENV
|
||||
@@ -52,7 +53,7 @@ func GetGlobalID(ctx context.Context) (globalID [8]byte) {
|
||||
return
|
||||
}
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.Network == net.Network_UDP &&
|
||||
(inbound.Name == "dokodemo-door" || inbound.Name == "socks" || inbound.Name == "shadowsocks") {
|
||||
(inbound.Name == "dokodemo-door" || inbound.Name == "socks" || inbound.Name == "shadowsocks" || inbound.Name == "tun") {
|
||||
h := blake3.New(8, BaseKey)
|
||||
h.Write([]byte(inbound.Source.String()))
|
||||
copy(globalID[:], h.Sum(nil))
|
||||
|
||||
+5
-1
@@ -64,7 +64,11 @@ func GetMergedConfig(args cmdarg.Arg) (string, error) {
|
||||
var files []*ConfigSource
|
||||
supported := []string{"json", "yaml", "toml"}
|
||||
for _, file := range args {
|
||||
format := GetFormat(file)
|
||||
format := "json"
|
||||
if file != "stdin:" {
|
||||
format = GetFormat(file)
|
||||
}
|
||||
|
||||
if slices.Contains(supported, format) {
|
||||
files = append(files, &ConfigSource{
|
||||
Name: file,
|
||||
|
||||
+3
-3
@@ -17,9 +17,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
Version_x byte = 25
|
||||
Version_y byte = 12
|
||||
Version_z byte = 8
|
||||
Version_x byte = 26
|
||||
Version_y byte = 1
|
||||
Version_z byte = 23
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -16,6 +16,7 @@ type Router interface {
|
||||
PickRoute(ctx Context) (Route, error)
|
||||
AddRule(config *serial.TypedMessage, shouldAppend bool) error
|
||||
RemoveRule(tag string) error
|
||||
ListRule() []Route
|
||||
}
|
||||
|
||||
// Route is the routing result of Router feature.
|
||||
@@ -65,6 +66,11 @@ func (DefaultRouter) RemoveRule(tag string) error {
|
||||
return common.ErrNoClue
|
||||
}
|
||||
|
||||
// ListRule implements Router.
|
||||
func (DefaultRouter) ListRule() []Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start implements common.Runnable.
|
||||
func (DefaultRouter) Start() error {
|
||||
return nil
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
module github.com/xtls/xray-core
|
||||
|
||||
go 1.25
|
||||
go 1.25.6
|
||||
|
||||
require (
|
||||
github.com/cloudflare/circl v1.6.2
|
||||
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178
|
||||
github.com/cloudflare/circl v1.6.3
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
||||
github.com/golang/mock v1.7.0-rc.1
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/miekg/dns v1.1.69
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pires/go-proxyproto v0.8.1
|
||||
github.com/quic-go/quic-go v0.58.0
|
||||
github.com/refraction-networking/utls v1.8.1
|
||||
github.com/pires/go-proxyproto v0.9.2
|
||||
github.com/refraction-networking/utls v1.8.2
|
||||
github.com/sagernet/sing v0.5.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/net v0.48.0
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/net v0.49.0
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sys v0.39.0
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
google.golang.org/grpc v1.78.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5
|
||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
||||
h12.io/socks v1.0.3
|
||||
lukechampine.com/blake3 v1.4.1
|
||||
)
|
||||
@@ -36,7 +36,6 @@ require (
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/juju/ratelimit v1.0.2 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
@@ -44,13 +43,11 @@ require (
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/vishvananda/netns v0.0.5 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/mod v0.31.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.org/x/tools v0.40.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=
|
||||
github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 h1:bSq8n+gX4oO/qnM3MKf4kroW75n+phO9Qp6nigJKZ1E=
|
||||
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178/go.mod h1:N1WIjPphkqs4efXWuyDNQ6OjjIK04vM3h+bEgwV+eVU=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 h1:BS21ZUJ/B5X2UVUbczfmdWH7GapPWAhxcMsDnjJTU1E=
|
||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
@@ -38,38 +37,28 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
|
||||
github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pires/go-proxyproto v0.9.2 h1:H1UdHn695zUVVmB0lQ354lOWHOy6TZSpzBl3tgN0s1U=
|
||||
github.com/pires/go-proxyproto v0.9.2/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug=
|
||||
github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/refraction-networking/utls v1.8.1 h1:yNY1kapmQU8JeM1sSw2H2asfTIwWxIkrMJI0pRUOCAo=
|
||||
github.com/refraction-networking/utls v1.8.1/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo=
|
||||
github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
|
||||
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
||||
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
|
||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||
@@ -95,16 +84,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
|
||||
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
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/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
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-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
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.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
@@ -116,21 +107,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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
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.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.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
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/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.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||
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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -152,12 +143,10 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 h1:sfK5nHuG7lRFZ2FdTT3RimOqWBg8IrVm+/Vko1FVOsk=
|
||||
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
|
||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 h1:Lk6hARj5UPY47dBep70OD/TIMwikJ5fGUGX0Rm3Xigk=
|
||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q=
|
||||
h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo=
|
||||
h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
||||
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package duration
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
+4
-4
@@ -1,20 +1,20 @@
|
||||
package duration_test
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/infra/conf/cfgcommon/duration"
|
||||
"github.com/xtls/xray-core/infra/conf/cfgcommon/types"
|
||||
)
|
||||
|
||||
type testWithDuration struct {
|
||||
Duration duration.Duration
|
||||
Duration types.Duration
|
||||
}
|
||||
|
||||
func TestDurationJSON(t *testing.T) {
|
||||
expected := &testWithDuration{
|
||||
Duration: duration.Duration(time.Hour),
|
||||
Duration: types.Duration(time.Hour),
|
||||
}
|
||||
data, err := json.Marshal(expected)
|
||||
if err != nil {
|
||||
@@ -0,0 +1,47 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Listable allows a field to be unmarshalled from a single object or a list of objects.
|
||||
// If the json input is a single object, it will be stored as a slice with one element.
|
||||
// If the json input is null or empty or a single empty object, it will be nil.
|
||||
type Listable[T any] []T
|
||||
|
||||
func (l *Listable[T]) UnmarshalJSON(data []byte) error {
|
||||
var v T
|
||||
if len(data) != 0 && !slices.Equal(data, []byte("null")) && data[0] != '[' {
|
||||
if err := json.Unmarshal(data, &v); err == nil {
|
||||
// make the list nil if the single value is the zero value
|
||||
var zero T
|
||||
if reflect.DeepEqual(v, zero) {
|
||||
return nil
|
||||
}
|
||||
*l = []T{v}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return json.Unmarshal(data, (*[]T)(l))
|
||||
}
|
||||
|
||||
// ListableSimpleString is like Listable[string], but able to separate by `~`
|
||||
type ListableSimpleString []string
|
||||
|
||||
func (l *ListableSimpleString) UnmarshalJSON(data []byte) error {
|
||||
var v string
|
||||
if len(data) != 0 && !slices.Equal(data, []byte("null")) && data[0] != '[' {
|
||||
if err := json.Unmarshal(data, &v); err == nil {
|
||||
if v == "" {
|
||||
// make the list nil if the single value is empty string
|
||||
return nil
|
||||
}
|
||||
*l = strings.Split(v, "~")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return json.Unmarshal(data, (*[]string)(l))
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/xtls/xray-core/infra/conf/cfgcommon/types"
|
||||
)
|
||||
|
||||
type TestGroup[T any] struct {
|
||||
name string
|
||||
input string
|
||||
expected []T
|
||||
}
|
||||
|
||||
// intentionally to be so chaos
|
||||
var rawJson = `{
|
||||
"field":
|
||||
["value1",
|
||||
"value2", "value3"
|
||||
]
|
||||
}`
|
||||
|
||||
func TestListableUnmarshal(t *testing.T) {
|
||||
type TestStruct struct {
|
||||
Field types.Listable[string] `json:"field"`
|
||||
}
|
||||
|
||||
tests := []TestGroup[string]{
|
||||
{
|
||||
name: "SingleString",
|
||||
input: `{"field": "hello"}`,
|
||||
expected: []string{"hello"},
|
||||
},
|
||||
{
|
||||
name: "ArrayString",
|
||||
input: `{"field": ["value1", "value2", "value3"]}`,
|
||||
expected: []string{"value1", "value2", "value3"},
|
||||
},
|
||||
{
|
||||
name: "ComplexArray",
|
||||
input: rawJson,
|
||||
expected: []string{"value1", "value2", "value3"},
|
||||
},
|
||||
{
|
||||
name: "SingleStringWithSpace",
|
||||
input: `{"field": "hello" }`,
|
||||
expected: []string{"hello"},
|
||||
},
|
||||
{
|
||||
name: "ArrayWithSpace",
|
||||
input: `{"field": [ "a", "b" ] }`,
|
||||
expected: []string{"a", "b"},
|
||||
},
|
||||
{
|
||||
name: "SingleEmptyString",
|
||||
input: `{"field": ""}`,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "Null",
|
||||
input: `{"field": null}`,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "Missing (default)",
|
||||
input: `{}`,
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var ts TestStruct
|
||||
err := json.Unmarshal([]byte(tt.input), &ts)
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
if !slices.Equal([]string(ts.Field), tt.expected) {
|
||||
t.Errorf("Expected %v, got %v", tt.expected, ts.Field)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListableInt(t *testing.T) {
|
||||
tests := []TestGroup[int]{
|
||||
{
|
||||
name: "SingleInt",
|
||||
input: `123`,
|
||||
expected: []int{123},
|
||||
},
|
||||
{
|
||||
name: "ArrayInt",
|
||||
input: `[1, 2]`,
|
||||
expected: []int{1, 2},
|
||||
},
|
||||
{
|
||||
name: "Null",
|
||||
input: `null`,
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var l types.Listable[int]
|
||||
err := json.Unmarshal([]byte(tt.input), &l)
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
if !slices.Equal([]int(l), tt.expected) {
|
||||
t.Errorf("Expected %v, got %v", tt.expected, l)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListableSimpleString(t *testing.T) {
|
||||
type TestStruct struct {
|
||||
Field types.ListableSimpleString `json:"field"`
|
||||
}
|
||||
|
||||
tests := []TestGroup[string]{
|
||||
{
|
||||
name: "SingleString",
|
||||
input: `{"field": "singleValue"}`,
|
||||
expected: []string{"singleValue"},
|
||||
},
|
||||
{
|
||||
name: "ArrayString",
|
||||
input: `{"field": ["value1", "value2", "value3"]}`,
|
||||
expected: []string{"value1", "value2", "value3"},
|
||||
},
|
||||
{
|
||||
name: "SingleEmptyString",
|
||||
input: `{"field": ""}`,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "WaveSplit",
|
||||
input: `{"field": "value1~value2~value3"}`,
|
||||
expected: []string{"value1", "value2", "value3"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var ts TestStruct
|
||||
err := json.Unmarshal([]byte(tt.input), &ts)
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
if !slices.Equal([]string(ts.Field), tt.expected) {
|
||||
t.Errorf("Expected %v, got %v", tt.expected, ts.Field)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+17
-2
@@ -80,6 +80,21 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
||||
return errors.New("failed to parse name server: ", string(data))
|
||||
}
|
||||
|
||||
func toDomainMatchingType(t router.Domain_Type) dns.DomainMatchingType {
|
||||
switch t {
|
||||
case router.Domain_Domain:
|
||||
return dns.DomainMatchingType_Subdomain
|
||||
case router.Domain_Full:
|
||||
return dns.DomainMatchingType_Full
|
||||
case router.Domain_Plain:
|
||||
return dns.DomainMatchingType_Keyword
|
||||
case router.Domain_Regex:
|
||||
return dns.DomainMatchingType_Regex
|
||||
default:
|
||||
panic("unknown domain type")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *NameServerConfig) Build() (*dns.NameServer, error) {
|
||||
if c.Address == nil {
|
||||
return nil, errors.New("NameServer address is not specified.")
|
||||
@@ -89,14 +104,14 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
|
||||
var originalRules []*dns.NameServer_OriginalRule
|
||||
|
||||
for _, rule := range c.Domains {
|
||||
parsedDomain, err := ParseDomainRule(rule)
|
||||
parsedDomain, err := parseDomainRule(rule)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid domain rule: ", rule).Base(err)
|
||||
}
|
||||
|
||||
for _, pd := range parsedDomain {
|
||||
domains = append(domains, &dns.NameServer_PriorityDomain{
|
||||
Type: dns.ToDomainMatchingType(pd.Type),
|
||||
Type: toDomainMatchingType(pd.Type),
|
||||
Domain: pd.Value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/proxy/hysteria"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type HysteriaClientConfig struct {
|
||||
Version int32 `json:"version"`
|
||||
Address *Address `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
}
|
||||
|
||||
func (c *HysteriaClientConfig) Build() (proto.Message, error) {
|
||||
if c.Version != 2 {
|
||||
return nil, errors.New("version != 2")
|
||||
}
|
||||
|
||||
config := &hysteria.ClientConfig{}
|
||||
config.Version = c.Version
|
||||
config.Server = &protocol.ServerEndpoint{
|
||||
Address: c.Address.Build(),
|
||||
Port: uint32(c.Port),
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
@@ -6,14 +6,14 @@ import (
|
||||
"github.com/xtls/xray-core/app/observatory"
|
||||
"github.com/xtls/xray-core/app/observatory/burst"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/infra/conf/cfgcommon/duration"
|
||||
"github.com/xtls/xray-core/infra/conf/cfgcommon/types"
|
||||
)
|
||||
|
||||
type ObservatoryConfig struct {
|
||||
SubjectSelector []string `json:"subjectSelector"`
|
||||
ProbeURL string `json:"probeURL"`
|
||||
ProbeInterval duration.Duration `json:"probeInterval"`
|
||||
EnableConcurrency bool `json:"enableConcurrency"`
|
||||
SubjectSelector []string `json:"subjectSelector"`
|
||||
ProbeURL string `json:"probeURL"`
|
||||
ProbeInterval types.Duration `json:"probeInterval"`
|
||||
EnableConcurrency bool `json:"enableConcurrency"`
|
||||
}
|
||||
|
||||
func (o *ObservatoryConfig) Build() (proto.Message, error) {
|
||||
|
||||
+152
-84
@@ -1,7 +1,10 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -102,7 +105,7 @@ func (c *RouterConfig) Build() (*router.Config, error) {
|
||||
}
|
||||
|
||||
for _, rawRule := range rawRuleList {
|
||||
rule, err := ParseRule(rawRule)
|
||||
rule, err := parseRule(rawRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -125,7 +128,7 @@ type RouterRule struct {
|
||||
BalancerTag string `json:"balancerTag"`
|
||||
}
|
||||
|
||||
func ParseIP(s string) (*router.CIDR, error) {
|
||||
func parseIP(s string) (*router.CIDR, error) {
|
||||
var addr, mask string
|
||||
i := strings.Index(s, "/")
|
||||
if i < 0 {
|
||||
@@ -173,91 +176,161 @@ func ParseIP(s string) (*router.CIDR, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func loadGeoIP(code string) ([]*router.CIDR, error) {
|
||||
return loadIP("geoip.dat", code)
|
||||
}
|
||||
|
||||
var (
|
||||
FileCache = make(map[string][]byte)
|
||||
IPCache = make(map[string]*router.GeoIP)
|
||||
SiteCache = make(map[string]*router.GeoSite)
|
||||
)
|
||||
|
||||
func loadFile(file string) ([]byte, error) {
|
||||
if FileCache[file] == nil {
|
||||
bs, err := filesystem.ReadAsset(file)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to open file: ", file).Base(err)
|
||||
}
|
||||
if len(bs) == 0 {
|
||||
return nil, errors.New("empty file: ", file)
|
||||
}
|
||||
// Do not cache file, may save RAM when there
|
||||
// are many files, but consume CPU each time.
|
||||
return bs, nil
|
||||
FileCache[file] = bs
|
||||
func loadFile(file, code string) ([]byte, error) {
|
||||
runtime.GC()
|
||||
r, err := filesystem.OpenAsset(file)
|
||||
defer r.Close()
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to open file: ", file).Base(err)
|
||||
}
|
||||
return FileCache[file], nil
|
||||
bs := find(r, []byte(code))
|
||||
if bs == nil {
|
||||
return nil, errors.New("code not found in ", file, ": ", code)
|
||||
}
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
func loadIP(file, code string) ([]*router.CIDR, error) {
|
||||
index := file + ":" + code
|
||||
if IPCache[index] == nil {
|
||||
var geoip router.GeoIP
|
||||
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
||||
// dont pass code becuase we have country code in top level router.GeoIP
|
||||
geoip = router.GeoIP{Cidr: []*router.CIDR{}}
|
||||
} else {
|
||||
bs, err := loadFile(file)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to load file: ", file).Base(err)
|
||||
}
|
||||
bs = filesystem.Find(bs, []byte(code))
|
||||
if bs == nil {
|
||||
return nil, errors.New("code not found in ", file, ": ", code)
|
||||
}
|
||||
if err := proto.Unmarshal(bs, &geoip); err != nil {
|
||||
return nil, errors.New("error unmarshal IP in ", file, ": ", code).Base(err)
|
||||
}
|
||||
}
|
||||
defer runtime.GC() // or debug.FreeOSMemory()
|
||||
return geoip.Cidr, nil // do not cache geoip
|
||||
IPCache[index] = &geoip
|
||||
bs, err := loadFile(file, code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return IPCache[index].Cidr, nil
|
||||
var geoip router.GeoIP
|
||||
if err := proto.Unmarshal(bs, &geoip); err != nil {
|
||||
return nil, errors.New("error unmarshal IP in ", file, ": ", code).Base(err)
|
||||
}
|
||||
defer runtime.GC() // or debug.FreeOSMemory()
|
||||
return geoip.Cidr, nil
|
||||
}
|
||||
|
||||
func loadSite(file, code string) ([]*router.Domain, error) {
|
||||
index := file + ":" + code
|
||||
if SiteCache[index] == nil {
|
||||
var geosite router.GeoSite
|
||||
bs, err := loadFile(file, code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var geosite router.GeoSite
|
||||
if err := proto.Unmarshal(bs, &geosite); err != nil {
|
||||
return nil, errors.New("error unmarshal Site in ", file, ": ", code).Base(err)
|
||||
}
|
||||
defer runtime.GC() // or debug.FreeOSMemory()
|
||||
return geosite.Domain, nil
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
||||
// pass file:code so can build optimized matcher later
|
||||
domain := router.Domain{Value: file + "_" + code}
|
||||
geosite = router.GeoSite{Domain: []*router.Domain{&domain}}
|
||||
func decodeVarint(r *bufio.Reader) (uint64, error) {
|
||||
var x uint64
|
||||
for shift := uint(0); shift < 64; shift += 7 {
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
x |= (uint64(b) & 0x7F) << shift
|
||||
if (b & 0x80) == 0 {
|
||||
return x, nil
|
||||
}
|
||||
}
|
||||
// The number is too large to represent in a 64-bit value.
|
||||
return 0, errors.New("varint overflow")
|
||||
}
|
||||
|
||||
} else {
|
||||
func find(r io.Reader, code []byte) []byte {
|
||||
codeL := len(code)
|
||||
if codeL == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
bs, err := loadFile(file)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to load file: ", file).Base(err)
|
||||
}
|
||||
bs = filesystem.Find(bs, []byte(code))
|
||||
if bs == nil {
|
||||
return nil, errors.New("list not found in ", file, ": ", code)
|
||||
}
|
||||
if err := proto.Unmarshal(bs, &geosite); err != nil {
|
||||
return nil, errors.New("error unmarshal Site in ", file, ": ", code).Base(err)
|
||||
br := bufio.NewReaderSize(r, 64*1024)
|
||||
need := 2 + codeL
|
||||
prefixBuf := make([]byte, need)
|
||||
|
||||
for {
|
||||
if _, err := br.ReadByte(); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
x, err := decodeVarint(br)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
bodyL := int(x)
|
||||
if bodyL <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
prefixL := bodyL
|
||||
if prefixL > need {
|
||||
prefixL = need
|
||||
}
|
||||
prefix := prefixBuf[:prefixL]
|
||||
if _, err := io.ReadFull(br, prefix); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
match := false
|
||||
if bodyL >= need {
|
||||
if int(prefix[1]) == codeL && bytes.Equal(prefix[2:need], code) {
|
||||
match = true
|
||||
}
|
||||
}
|
||||
|
||||
defer runtime.GC() // or debug.FreeOSMemory()
|
||||
return geosite.Domain, nil // do not cache geosite
|
||||
SiteCache[index] = &geosite
|
||||
remain := bodyL - prefixL
|
||||
if match {
|
||||
out := make([]byte, bodyL)
|
||||
copy(out, prefix)
|
||||
if remain > 0 {
|
||||
if _, err := io.ReadFull(br, out[prefixL:]); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
if remain > 0 {
|
||||
if _, err := br.Discard(remain); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return SiteCache[index].Domain, nil
|
||||
}
|
||||
|
||||
type AttributeMatcher interface {
|
||||
Match(*router.Domain) bool
|
||||
}
|
||||
|
||||
type BooleanMatcher string
|
||||
|
||||
func (m BooleanMatcher) Match(domain *router.Domain) bool {
|
||||
for _, attr := range domain.Attribute {
|
||||
if attr.Key == string(m) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type AttributeList struct {
|
||||
matcher []AttributeMatcher
|
||||
}
|
||||
|
||||
func (al *AttributeList) Match(domain *router.Domain) bool {
|
||||
for _, matcher := range al.matcher {
|
||||
if !matcher.Match(domain) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (al *AttributeList) IsEmpty() bool {
|
||||
return len(al.matcher) == 0
|
||||
}
|
||||
|
||||
func parseAttrs(attrs []string) *AttributeList {
|
||||
al := new(AttributeList)
|
||||
for _, attr := range attrs {
|
||||
lc := strings.ToLower(attr)
|
||||
al.matcher = append(al.matcher, BooleanMatcher(lc))
|
||||
}
|
||||
return al
|
||||
}
|
||||
|
||||
func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
|
||||
@@ -266,7 +339,7 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er
|
||||
return nil, errors.New("empty site")
|
||||
}
|
||||
country := strings.ToUpper(parts[0])
|
||||
attrs := router.ParseAttrs(parts[1:])
|
||||
attrs := parseAttrs(parts[1:])
|
||||
domains, err := loadSite(file, country)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -276,11 +349,6 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
|
||||
domains[0].Value = domains[0].Value + "_" + strings.Join(parts[1:], ",")
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
filteredDomains := make([]*router.Domain, 0, len(domains))
|
||||
for _, domain := range domains {
|
||||
if attrs.Match(domain) {
|
||||
@@ -291,7 +359,7 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er
|
||||
return filteredDomains, nil
|
||||
}
|
||||
|
||||
func ParseDomainRule(domain string) ([]*router.Domain, error) {
|
||||
func parseDomainRule(domain string) ([]*router.Domain, error) {
|
||||
if strings.HasPrefix(domain, "geosite:") {
|
||||
country := strings.ToUpper(domain[8:])
|
||||
domains, err := loadGeositeWithAttr("geosite.dat", country)
|
||||
@@ -376,7 +444,7 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) {
|
||||
if len(country) == 0 {
|
||||
return nil, errors.New("empty country name in rule")
|
||||
}
|
||||
geoip, err := loadGeoIP(strings.ToUpper(country))
|
||||
geoip, err := loadIP("geoip.dat", strings.ToUpper(country))
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to load GeoIP: ", country).Base(err)
|
||||
}
|
||||
@@ -430,7 +498,7 @@ func ToCidrList(ips StringList) ([]*router.GeoIP, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
ipRule, err := ParseIP(ip)
|
||||
ipRule, err := parseIP(ip)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid IP: ", ip).Base(err)
|
||||
}
|
||||
@@ -489,7 +557,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
|
||||
if rawFieldRule.Domain != nil {
|
||||
for _, domain := range *rawFieldRule.Domain {
|
||||
rules, err := ParseDomainRule(domain)
|
||||
rules, err := parseDomainRule(domain)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to parse domain rule: ", domain).Base(err)
|
||||
}
|
||||
@@ -499,7 +567,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
|
||||
if rawFieldRule.Domains != nil {
|
||||
for _, domain := range *rawFieldRule.Domains {
|
||||
rules, err := ParseDomainRule(domain)
|
||||
rules, err := parseDomainRule(domain)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to parse domain rule: ", domain).Base(err)
|
||||
}
|
||||
@@ -584,7 +652,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
func parseRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
rawRule := new(RouterRule)
|
||||
err := json.Unmarshal(msg, rawRule)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/xtls/xray-core/app/observatory/burst"
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/infra/conf/cfgcommon/duration"
|
||||
"github.com/xtls/xray-core/infra/conf/cfgcommon/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,23 +37,23 @@ type strategyLeastLoadConfig struct {
|
||||
// weight settings
|
||||
Costs []*router.StrategyWeight `json:"costs,omitempty"`
|
||||
// ping rtt baselines
|
||||
Baselines []duration.Duration `json:"baselines,omitempty"`
|
||||
Baselines []types.Duration `json:"baselines,omitempty"`
|
||||
// expected nodes count to select
|
||||
Expected int32 `json:"expected,omitempty"`
|
||||
// max acceptable rtt, filter away high delay nodes. default 0
|
||||
MaxRTT duration.Duration `json:"maxRTT,omitempty"`
|
||||
MaxRTT types.Duration `json:"maxRTT,omitempty"`
|
||||
// acceptable failure rate
|
||||
Tolerance float64 `json:"tolerance,omitempty"`
|
||||
}
|
||||
|
||||
// healthCheckSettings holds settings for health Checker
|
||||
type healthCheckSettings struct {
|
||||
Destination string `json:"destination"`
|
||||
Connectivity string `json:"connectivity"`
|
||||
Interval duration.Duration `json:"interval"`
|
||||
SamplingCount int `json:"sampling"`
|
||||
Timeout duration.Duration `json:"timeout"`
|
||||
HttpMethod string `json:"httpMethod"`
|
||||
Destination string `json:"destination"`
|
||||
Connectivity string `json:"connectivity"`
|
||||
Interval types.Duration `json:"interval"`
|
||||
SamplingCount int `json:"sampling"`
|
||||
Timeout types.Duration `json:"timeout"`
|
||||
HttpMethod string `json:"httpMethod"`
|
||||
}
|
||||
|
||||
func (h healthCheckSettings) Build() (proto.Message, error) {
|
||||
|
||||
@@ -46,10 +46,11 @@ type ShadowsocksServerConfig struct {
|
||||
Email string `json:"email"`
|
||||
Users []*ShadowsocksUserConfig `json:"clients"`
|
||||
NetworkList *NetworkList `json:"network"`
|
||||
IVCheck bool `json:"ivCheck"`
|
||||
}
|
||||
|
||||
func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("Shadowsocks (with no Forward Secrecy, etc.)", "VLESS Encryption")
|
||||
|
||||
if C.Contains(shadowaead_2022.List, v.Cipher) {
|
||||
return buildShadowsocks2022(v)
|
||||
}
|
||||
@@ -62,7 +63,6 @@ func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
|
||||
account := &shadowsocks.Account{
|
||||
Password: user.Password,
|
||||
CipherType: cipherFromString(user.Cipher),
|
||||
IvCheck: v.IVCheck,
|
||||
}
|
||||
if account.Password == "" {
|
||||
return nil, errors.New("Shadowsocks password is not specified.")
|
||||
@@ -81,7 +81,6 @@ func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
|
||||
account := &shadowsocks.Account{
|
||||
Password: v.Password,
|
||||
CipherType: cipherFromString(v.Cipher),
|
||||
IvCheck: v.IVCheck,
|
||||
}
|
||||
if account.Password == "" {
|
||||
return nil, errors.New("Shadowsocks password is not specified.")
|
||||
@@ -166,7 +165,6 @@ type ShadowsocksServerTarget struct {
|
||||
Email string `json:"email"`
|
||||
Cipher string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
IVCheck bool `json:"ivCheck"`
|
||||
UoT bool `json:"uot"`
|
||||
UoTVersion int `json:"uotVersion"`
|
||||
}
|
||||
@@ -178,13 +176,14 @@ type ShadowsocksClientConfig struct {
|
||||
Email string `json:"email"`
|
||||
Cipher string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
IVCheck bool `json:"ivCheck"`
|
||||
UoT bool `json:"uot"`
|
||||
UoTVersion int `json:"uotVersion"`
|
||||
Servers []*ShadowsocksServerTarget `json:"servers"`
|
||||
}
|
||||
|
||||
func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("Shadowsocks (with no Forward Secrecy, etc.)", "VLESS Encryption")
|
||||
|
||||
if v.Address != nil {
|
||||
v.Servers = []*ShadowsocksServerTarget{
|
||||
{
|
||||
@@ -194,7 +193,6 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
|
||||
Email: v.Email,
|
||||
Cipher: v.Cipher,
|
||||
Password: v.Password,
|
||||
IVCheck: v.IVCheck,
|
||||
UoT: v.UoT,
|
||||
UoTVersion: v.UoTVersion,
|
||||
},
|
||||
@@ -250,12 +248,10 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New("unknown cipher method: ", server.Cipher)
|
||||
}
|
||||
|
||||
account.IvCheck = server.IVCheck
|
||||
|
||||
ss := &protocol.ServerEndpoint{
|
||||
Address: server.Address.Build(),
|
||||
Port: uint32(server.Port),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Level: uint32(server.Level),
|
||||
Email: server.Email,
|
||||
Account: serial.ToTypedMessage(account),
|
||||
|
||||
@@ -16,7 +16,9 @@ import (
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
||||
"github.com/xtls/xray-core/transport/internet/httpupgrade"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria"
|
||||
"github.com/xtls/xray-core/transport/internet/kcp"
|
||||
"github.com/xtls/xray-core/transport/internet/reality"
|
||||
"github.com/xtls/xray-core/transport/internet/splithttp"
|
||||
@@ -332,6 +334,177 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
const (
|
||||
Byte = 1
|
||||
Kilobyte = 1024 * Byte
|
||||
Megabyte = 1024 * Kilobyte
|
||||
Gigabyte = 1024 * Megabyte
|
||||
Terabyte = 1024 * Gigabyte
|
||||
)
|
||||
|
||||
type Bandwidth string
|
||||
|
||||
func (b Bandwidth) Bps() (uint64, error) {
|
||||
s := strings.TrimSpace(strings.ToLower(string(b)))
|
||||
if s == "" {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
idx := len(s)
|
||||
for i, c := range s {
|
||||
if (c < '0' || c > '9') && c != '.' {
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
numStr := s[:idx]
|
||||
unit := strings.TrimSpace(s[idx:])
|
||||
|
||||
val, err := strconv.ParseFloat(numStr, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
mul := uint64(1)
|
||||
switch unit {
|
||||
case "", "b", "bps":
|
||||
mul = Byte
|
||||
case "k", "kb", "kbps":
|
||||
mul = Kilobyte
|
||||
case "m", "mb", "mbps":
|
||||
mul = Megabyte
|
||||
case "g", "gb", "gbps":
|
||||
mul = Gigabyte
|
||||
case "t", "tb", "tbps":
|
||||
mul = Terabyte
|
||||
default:
|
||||
return 0, errors.New("unsupported unit: " + unit)
|
||||
}
|
||||
|
||||
return uint64(val*float64(mul)) / 8, nil
|
||||
}
|
||||
|
||||
type UdpHop struct {
|
||||
PortList json.RawMessage `json:"port"`
|
||||
Interval *Int32Range `json:"interval"`
|
||||
}
|
||||
|
||||
type HysteriaConfig struct {
|
||||
Version int32 `json:"version"`
|
||||
Auth string `json:"auth"`
|
||||
Congestion string `json:"congestion"`
|
||||
Up Bandwidth `json:"up"`
|
||||
Down Bandwidth `json:"down"`
|
||||
UdpHop UdpHop `json:"udphop"`
|
||||
|
||||
InitStreamReceiveWindow uint64 `json:"initStreamReceiveWindow"`
|
||||
MaxStreamReceiveWindow uint64 `json:"maxStreamReceiveWindow"`
|
||||
InitConnectionReceiveWindow uint64 `json:"initConnectionReceiveWindow"`
|
||||
MaxConnectionReceiveWindow uint64 `json:"maxConnectionReceiveWindow"`
|
||||
MaxIdleTimeout int64 `json:"maxIdleTimeout"`
|
||||
KeepAlivePeriod int64 `json:"keepAlivePeriod"`
|
||||
DisablePathMTUDiscovery bool `json:"disablePathMTUDiscovery"`
|
||||
}
|
||||
|
||||
func (c *HysteriaConfig) Build() (proto.Message, error) {
|
||||
if c.Version != 2 {
|
||||
return nil, errors.New("version != 2")
|
||||
}
|
||||
|
||||
up, err := c.Up.Bps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
down, err := c.Down.Bps()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.Congestion = strings.ToLower(c.Congestion)
|
||||
if c.Congestion == "force-brutal" && up == 0 {
|
||||
return nil, errors.New("force-brutal require up")
|
||||
}
|
||||
|
||||
var hop *PortList
|
||||
if err := json.Unmarshal(c.UdpHop.PortList, &hop); err != nil {
|
||||
hop = &PortList{}
|
||||
}
|
||||
|
||||
var inertvalMin, inertvalMax int64
|
||||
if c.UdpHop.Interval != nil {
|
||||
inertvalMin = int64(c.UdpHop.Interval.From)
|
||||
inertvalMax = int64(c.UdpHop.Interval.To)
|
||||
}
|
||||
|
||||
if up > 0 && up < 65536 {
|
||||
return nil, errors.New("Up must be at least 65536 Bps")
|
||||
}
|
||||
if down > 0 && down < 65536 {
|
||||
return nil, errors.New("Down must be at least 65536 Bps")
|
||||
}
|
||||
if (inertvalMin != 0 && inertvalMin < 5) || (inertvalMax != 0 && inertvalMax < 5) {
|
||||
return nil, errors.New("Interval must be at least 5")
|
||||
}
|
||||
|
||||
if c.InitStreamReceiveWindow > 0 && c.InitStreamReceiveWindow < 16384 {
|
||||
return nil, errors.New("InitStreamReceiveWindow must be at least 16384")
|
||||
}
|
||||
if c.MaxStreamReceiveWindow > 0 && c.MaxStreamReceiveWindow < 16384 {
|
||||
return nil, errors.New("MaxStreamReceiveWindow must be at least 16384")
|
||||
}
|
||||
if c.InitConnectionReceiveWindow > 0 && c.InitConnectionReceiveWindow < 16384 {
|
||||
return nil, errors.New("InitConnectionReceiveWindow must be at least 16384")
|
||||
}
|
||||
if c.MaxConnectionReceiveWindow > 0 && c.MaxConnectionReceiveWindow < 16384 {
|
||||
return nil, errors.New("MaxConnectionReceiveWindow must be at least 16384")
|
||||
}
|
||||
if c.MaxIdleTimeout != 0 && (c.MaxIdleTimeout < 4 || c.MaxIdleTimeout > 120) {
|
||||
return nil, errors.New("MaxIdleTimeout must be between 4 and 120")
|
||||
}
|
||||
if c.KeepAlivePeriod != 0 && (c.KeepAlivePeriod < 2 || c.KeepAlivePeriod > 60) {
|
||||
return nil, errors.New("KeepAlivePeriod must be between 2 and 60")
|
||||
}
|
||||
|
||||
config := &hysteria.Config{}
|
||||
config.Version = c.Version
|
||||
config.Auth = c.Auth
|
||||
config.Congestion = c.Congestion
|
||||
config.Up = up
|
||||
config.Down = down
|
||||
config.Ports = hop.Build().Ports()
|
||||
config.IntervalMin = inertvalMin
|
||||
config.IntervalMax = inertvalMax
|
||||
config.InitStreamReceiveWindow = c.InitStreamReceiveWindow
|
||||
config.MaxStreamReceiveWindow = c.MaxStreamReceiveWindow
|
||||
config.InitConnReceiveWindow = c.InitConnectionReceiveWindow
|
||||
config.MaxConnReceiveWindow = c.MaxConnectionReceiveWindow
|
||||
config.MaxIdleTimeout = c.MaxIdleTimeout
|
||||
config.KeepAlivePeriod = c.KeepAlivePeriod
|
||||
config.DisablePathMtuDiscovery = c.DisablePathMTUDiscovery
|
||||
|
||||
if config.InitStreamReceiveWindow == 0 {
|
||||
config.InitStreamReceiveWindow = 8388608
|
||||
}
|
||||
if config.MaxStreamReceiveWindow == 0 {
|
||||
config.MaxStreamReceiveWindow = 8388608
|
||||
}
|
||||
if config.InitConnReceiveWindow == 0 {
|
||||
config.InitConnReceiveWindow = 8388608 * 5 / 2
|
||||
}
|
||||
if config.MaxConnReceiveWindow == 0 {
|
||||
config.MaxConnReceiveWindow = 8388608 * 5 / 2
|
||||
}
|
||||
if config.MaxIdleTimeout == 0 {
|
||||
config.MaxIdleTimeout = 30
|
||||
}
|
||||
// if config.KeepAlivePeriod == 0 {
|
||||
// config.KeepAlivePeriod = 10
|
||||
// }
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func readFileOrString(f string, s []string) ([]byte, error) {
|
||||
if len(f) > 0 {
|
||||
return filesystem.ReadCert(f)
|
||||
@@ -395,27 +568,26 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
Insecure bool `json:"allowInsecure"`
|
||||
Certs []*TLSCertConfig `json:"certificates"`
|
||||
ServerName string `json:"serverName"`
|
||||
ALPN *StringList `json:"alpn"`
|
||||
EnableSessionResumption bool `json:"enableSessionResumption"`
|
||||
DisableSystemRoot bool `json:"disableSystemRoot"`
|
||||
MinVersion string `json:"minVersion"`
|
||||
MaxVersion string `json:"maxVersion"`
|
||||
CipherSuites string `json:"cipherSuites"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
RejectUnknownSNI bool `json:"rejectUnknownSni"`
|
||||
PinnedPeerCertificateChainSha256 *[]string `json:"pinnedPeerCertificateChainSha256"`
|
||||
PinnedPeerCertificatePublicKeySha256 *[]string `json:"pinnedPeerCertificatePublicKeySha256"`
|
||||
CurvePreferences *StringList `json:"curvePreferences"`
|
||||
MasterKeyLog string `json:"masterKeyLog"`
|
||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||
ECHServerKeys string `json:"echServerKeys"`
|
||||
ECHConfigList string `json:"echConfigList"`
|
||||
ECHForceQuery string `json:"echForceQuery"`
|
||||
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
||||
AllowInsecure bool `json:"allowInsecure"`
|
||||
Certs []*TLSCertConfig `json:"certificates"`
|
||||
ServerName string `json:"serverName"`
|
||||
ALPN *StringList `json:"alpn"`
|
||||
EnableSessionResumption bool `json:"enableSessionResumption"`
|
||||
DisableSystemRoot bool `json:"disableSystemRoot"`
|
||||
MinVersion string `json:"minVersion"`
|
||||
MaxVersion string `json:"maxVersion"`
|
||||
CipherSuites string `json:"cipherSuites"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
RejectUnknownSNI bool `json:"rejectUnknownSni"`
|
||||
CurvePreferences *StringList `json:"curvePreferences"`
|
||||
MasterKeyLog string `json:"masterKeyLog"`
|
||||
PinnedPeerCertSha256 string `json:"pinnedPeerCertSha256"`
|
||||
VerifyPeerCertByName string `json:"verifyPeerCertByName"`
|
||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||
ECHServerKeys string `json:"echServerKeys"`
|
||||
ECHConfigList string `json:"echConfigList"`
|
||||
ECHForceQuery string `json:"echForceQuery"`
|
||||
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -430,7 +602,6 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
config.Certificate[idx] = cert
|
||||
}
|
||||
serverName := c.ServerName
|
||||
config.AllowInsecure = c.Insecure
|
||||
if len(c.ServerName) > 0 {
|
||||
config.ServerName = serverName
|
||||
}
|
||||
@@ -457,35 +628,37 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New(`unknown "fingerprint": `, config.Fingerprint)
|
||||
}
|
||||
config.RejectUnknownSni = c.RejectUnknownSNI
|
||||
|
||||
if c.PinnedPeerCertificateChainSha256 != nil {
|
||||
config.PinnedPeerCertificateChainSha256 = [][]byte{}
|
||||
for _, v := range *c.PinnedPeerCertificateChainSha256 {
|
||||
hashValue, err := base64.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.PinnedPeerCertificateChainSha256 = append(config.PinnedPeerCertificateChainSha256, hashValue)
|
||||
}
|
||||
}
|
||||
|
||||
if c.PinnedPeerCertificatePublicKeySha256 != nil {
|
||||
config.PinnedPeerCertificatePublicKeySha256 = [][]byte{}
|
||||
for _, v := range *c.PinnedPeerCertificatePublicKeySha256 {
|
||||
hashValue, err := base64.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.PinnedPeerCertificatePublicKeySha256 = append(config.PinnedPeerCertificatePublicKeySha256, hashValue)
|
||||
}
|
||||
}
|
||||
|
||||
config.MasterKeyLog = c.MasterKeyLog
|
||||
|
||||
if c.ServerNameToVerify != "" {
|
||||
return nil, errors.PrintRemovedFeatureError(`"serverNameToVerify"`, `"verifyPeerCertInNames"`)
|
||||
if c.AllowInsecure {
|
||||
return nil, errors.PrintRemovedFeatureError(`"allowInsecure"`, `"pinnedPeerCertSha256"`)
|
||||
}
|
||||
if c.PinnedPeerCertSha256 != "" {
|
||||
for v := range strings.SplitSeq(c.PinnedPeerCertSha256, ",") {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
hashValue, err := hex.DecodeString(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.PinnedPeerCertSha256 = append(config.PinnedPeerCertSha256, hashValue)
|
||||
}
|
||||
}
|
||||
|
||||
if c.VerifyPeerCertInNames != nil {
|
||||
return nil, errors.PrintRemovedFeatureError(`"verifyPeerCertInNames"`, `"verifyPeerCertByName"`)
|
||||
}
|
||||
if c.VerifyPeerCertByName != "" {
|
||||
for v := range strings.SplitSeq(c.VerifyPeerCertByName, ",") {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
config.VerifyPeerCertByName = append(config.VerifyPeerCertByName, v)
|
||||
}
|
||||
}
|
||||
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
|
||||
|
||||
if c.ECHServerKeys != "" {
|
||||
EchPrivateKey, err := base64.StdEncoding.DecodeString(c.ECHServerKeys)
|
||||
@@ -740,18 +913,20 @@ func (p TransportProtocol) Build() (string, error) {
|
||||
case "kcp", "mkcp":
|
||||
return "mkcp", nil
|
||||
case "grpc":
|
||||
errors.PrintDeprecatedFeatureWarning("gRPC transport (with unnecessary costs, etc.)", "XHTTP stream-up H2")
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("gRPC transport (with unnecessary costs, etc.)", "XHTTP stream-up H2")
|
||||
return "grpc", nil
|
||||
case "ws", "websocket":
|
||||
errors.PrintDeprecatedFeatureWarning("WebSocket transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3")
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("WebSocket transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3")
|
||||
return "websocket", nil
|
||||
case "httpupgrade":
|
||||
errors.PrintDeprecatedFeatureWarning("HTTPUpgrade transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3")
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("HTTPUpgrade transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3")
|
||||
return "httpupgrade", nil
|
||||
case "h2", "h3", "http":
|
||||
return "", errors.PrintRemovedFeatureError("HTTP transport (without header padding, etc.)", "XHTTP stream-one H2 & H3")
|
||||
case "quic":
|
||||
return "", errors.PrintRemovedFeatureError("QUIC transport (without web service, etc.)", "XHTTP stream-one H3")
|
||||
case "hysteria":
|
||||
return "hysteria", nil
|
||||
default:
|
||||
return "", errors.New("Config: unknown transport protocol: ", p)
|
||||
}
|
||||
@@ -934,11 +1109,54 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||
"salamander": func() interface{} { return new(Salamander) },
|
||||
}, "type", "settings")
|
||||
)
|
||||
|
||||
type Salamander struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (c *Salamander) Build() (proto.Message, error) {
|
||||
config := &salamander.Config{}
|
||||
config.Password = c.Password
|
||||
return config, nil
|
||||
}
|
||||
|
||||
type FinalMask struct {
|
||||
Type string `json:"type"`
|
||||
Settings *json.RawMessage `json:"settings"`
|
||||
}
|
||||
|
||||
func (c *FinalMask) Build(tcpmaskLoader bool) (proto.Message, error) {
|
||||
loader := udpmaskLoader
|
||||
if tcpmaskLoader {
|
||||
return nil, errors.New("")
|
||||
}
|
||||
|
||||
settings := []byte("{}")
|
||||
if c.Settings != nil {
|
||||
settings = ([]byte)(*c.Settings)
|
||||
}
|
||||
rawConfig, err := loader.LoadWithID(settings, c.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts, err := rawConfig.(Buildable).Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
type StreamConfig struct {
|
||||
Address *Address `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
Network *TransportProtocol `json:"network"`
|
||||
Security string `json:"security"`
|
||||
Udpmasks []*FinalMask `json:"udpmasks"`
|
||||
TLSSettings *TLSConfig `json:"tlsSettings"`
|
||||
REALITYSettings *REALITYConfig `json:"realitySettings"`
|
||||
RAWSettings *TCPConfig `json:"rawSettings"`
|
||||
@@ -949,6 +1167,7 @@ type StreamConfig struct {
|
||||
GRPCSettings *GRPCConfig `json:"grpcSettings"`
|
||||
WSSettings *WebSocketConfig `json:"wsSettings"`
|
||||
HTTPUPGRADESettings *HttpUpgradeConfig `json:"httpupgradeSettings"`
|
||||
HysteriaSettings *HysteriaConfig `json:"hysteriaSettings"`
|
||||
SocketSettings *SocketConfig `json:"sockopt"`
|
||||
}
|
||||
|
||||
@@ -968,6 +1187,7 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
||||
}
|
||||
config.ProtocolName = protocol
|
||||
}
|
||||
|
||||
switch strings.ToLower(c.Security) {
|
||||
case "", "none":
|
||||
case "tls":
|
||||
@@ -1001,6 +1221,7 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
||||
default:
|
||||
return nil, errors.New(`Unknown security "` + c.Security + `".`)
|
||||
}
|
||||
|
||||
if c.RAWSettings != nil {
|
||||
c.TCPSettings = c.RAWSettings
|
||||
}
|
||||
@@ -1067,6 +1288,16 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
||||
Settings: serial.ToTypedMessage(hs),
|
||||
})
|
||||
}
|
||||
if c.HysteriaSettings != nil {
|
||||
hs, err := c.HysteriaSettings.Build()
|
||||
if err != nil {
|
||||
return nil, errors.New("Failed to build Hysteria config.").Base(err)
|
||||
}
|
||||
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
||||
ProtocolName: "hysteria",
|
||||
Settings: serial.ToTypedMessage(hs),
|
||||
})
|
||||
}
|
||||
if c.SocketSettings != nil {
|
||||
ss, err := c.SocketSettings.Build()
|
||||
if err != nil {
|
||||
@@ -1074,6 +1305,15 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
||||
}
|
||||
config.SocketSettings = ss
|
||||
}
|
||||
|
||||
for _, mask := range c.Udpmasks {
|
||||
u, err := mask.Build(false)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build mask with type ", mask.Type).Base(err)
|
||||
}
|
||||
config.Udpmasks = append(config.Udpmasks, serial.ToTypedMessage(u))
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ type TrojanClientConfig struct {
|
||||
|
||||
// Build implements Buildable
|
||||
func (c *TrojanClientConfig) Build() (proto.Message, error) {
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("Trojan (with no Flow, etc.)", "VLESS with Flow & Seed")
|
||||
|
||||
if c.Address != nil {
|
||||
c.Servers = []*TrojanServerTarget{
|
||||
{
|
||||
@@ -74,7 +76,7 @@ func (c *TrojanClientConfig) Build() (proto.Message, error) {
|
||||
config.Server = &protocol.ServerEndpoint{
|
||||
Address: rec.Address.Build(),
|
||||
Port: uint32(rec.Port),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Level: uint32(rec.Level),
|
||||
Email: rec.Email,
|
||||
Account: serial.ToTypedMessage(&trojan.Account{
|
||||
@@ -115,6 +117,8 @@ type TrojanServerConfig struct {
|
||||
|
||||
// Build implements Buildable
|
||||
func (c *TrojanServerConfig) Build() (proto.Message, error) {
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("Trojan (with no Flow, etc.)", "VLESS with Flow & Seed")
|
||||
|
||||
config := &trojan.ServerConfig{
|
||||
Users: make([]*protocol.User, len(c.Clients)),
|
||||
}
|
||||
|
||||
+6
-5
@@ -42,8 +42,6 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
config := new(inbound.Config)
|
||||
config.Clients = make([]*protocol.User, len(c.Clients))
|
||||
switch c.Flow {
|
||||
case vless.None:
|
||||
c.Flow = ""
|
||||
case "", vless.XRV:
|
||||
default:
|
||||
return nil, errors.New(`VLESS "settings.flow" doesn't support "` + c.Flow + `" in this version`)
|
||||
@@ -67,12 +65,13 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
switch account.Flow {
|
||||
case "":
|
||||
account.Flow = c.Flow
|
||||
case vless.None:
|
||||
account.Flow = ""
|
||||
case vless.XRV:
|
||||
default:
|
||||
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||
}
|
||||
if account.Flow == "" {
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("VLESS (with no Flow, etc.)", "VLESS with Flow & Seed")
|
||||
}
|
||||
|
||||
if len(account.Testseed) < 4 {
|
||||
account.Testseed = c.Testseed
|
||||
@@ -280,7 +279,9 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||
account.Id = u.String()
|
||||
|
||||
switch account.Flow {
|
||||
case "", vless.XRV, vless.XRV + "-udp443":
|
||||
case "":
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("VLESS (with no Flow, etc.)", "VLESS with Flow & Seed")
|
||||
case vless.XRV, vless.XRV + "-udp443":
|
||||
default:
|
||||
return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||
}
|
||||
|
||||
+6
-2
@@ -58,12 +58,14 @@ func (c *VMessDefaultConfig) Build() *inbound.DefaultConfig {
|
||||
}
|
||||
|
||||
type VMessInboundConfig struct {
|
||||
Users []json.RawMessage `json:"clients"`
|
||||
Defaults *VMessDefaultConfig `json:"default"`
|
||||
Users []json.RawMessage `json:"clients"`
|
||||
Defaults *VMessDefaultConfig `json:"default"`
|
||||
}
|
||||
|
||||
// Build implements Buildable
|
||||
func (c *VMessInboundConfig) Build() (proto.Message, error) {
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("VMess (with no Forward Secrecy, etc.)", "VLESS Encryption")
|
||||
|
||||
config := &inbound.Config{}
|
||||
|
||||
if c.Defaults != nil {
|
||||
@@ -113,6 +115,8 @@ type VMessOutboundConfig struct {
|
||||
|
||||
// Build implements Buildable
|
||||
func (c *VMessOutboundConfig) Build() (proto.Message, error) {
|
||||
errors.PrintNonRemovalDeprecatedFeatureWarning("VMess (with no Forward Secrecy, etc.)", "VLESS Encryption")
|
||||
|
||||
config := new(outbound.Config)
|
||||
if c.Address != nil {
|
||||
c.Receivers = []*VMessOutboundTarget{
|
||||
|
||||
+12
-8
@@ -43,6 +43,7 @@ var (
|
||||
"vless": func() interface{} { return new(VLessOutboundConfig) },
|
||||
"vmess": func() interface{} { return new(VMessOutboundConfig) },
|
||||
"trojan": func() interface{} { return new(TrojanClientConfig) },
|
||||
"hysteria": func() interface{} { return new(HysteriaClientConfig) },
|
||||
"dns": func() interface{} { return new(DNSOutboundConfig) },
|
||||
"wireguard": func() interface{} { return &WireGuardConfig{IsClient: true} },
|
||||
}, "protocol", "settings")
|
||||
@@ -117,20 +118,23 @@ func (m *MuxConfig) Build() (*proxyman.MultiplexingConfig, error) {
|
||||
}
|
||||
|
||||
type InboundDetourConfig struct {
|
||||
Protocol string `json:"protocol"`
|
||||
PortList *PortList `json:"port"`
|
||||
ListenOn *Address `json:"listen"`
|
||||
Settings *json.RawMessage `json:"settings"`
|
||||
Tag string `json:"tag"`
|
||||
StreamSetting *StreamConfig `json:"streamSettings"`
|
||||
SniffingConfig *SniffingConfig `json:"sniffing"`
|
||||
Protocol string `json:"protocol"`
|
||||
PortList *PortList `json:"port"`
|
||||
ListenOn *Address `json:"listen"`
|
||||
Settings *json.RawMessage `json:"settings"`
|
||||
Tag string `json:"tag"`
|
||||
StreamSetting *StreamConfig `json:"streamSettings"`
|
||||
SniffingConfig *SniffingConfig `json:"sniffing"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
|
||||
receiverSettings := &proxyman.ReceiverConfig{}
|
||||
|
||||
if c.ListenOn == nil {
|
||||
// TUN inbound doesn't need port configuration as it uses network interface instead
|
||||
if strings.ToLower(c.Protocol) == "tun" {
|
||||
// Skip port validation for TUN
|
||||
} else if c.ListenOn == nil {
|
||||
// Listen on anyip, must set PortList
|
||||
if c.PortList == nil {
|
||||
return nil, errors.New("Listen on AnyIP but no Port(s) set in InboundDetour.")
|
||||
|
||||
@@ -29,6 +29,7 @@ var CmdAPI = &base.Command{
|
||||
cmdInboundUserCount,
|
||||
cmdAddRules,
|
||||
cmdRemoveRules,
|
||||
cmdListRules,
|
||||
cmdSourceIpBlock,
|
||||
cmdOnlineStats,
|
||||
cmdOnlineStatsIpList,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
routerService "github.com/xtls/xray-core/app/router/command"
|
||||
"github.com/xtls/xray-core/main/commands/base"
|
||||
)
|
||||
|
||||
var cmdListRules = &base.Command{
|
||||
CustomFlags: true,
|
||||
UsageLine: "{{.Exec}} api lsrules [--server=127.0.0.1:8080]",
|
||||
Short: "List routing rules",
|
||||
Long: `
|
||||
List routing rules in Xray.
|
||||
|
||||
Arguments:
|
||||
|
||||
-s, -server <server:port>
|
||||
The API server address. Default 127.0.0.1:8080
|
||||
|
||||
-t, -timeout <seconds>
|
||||
Timeout in seconds for calling API. Default 3
|
||||
|
||||
Example:
|
||||
|
||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080
|
||||
`,
|
||||
Run: executeListRules,
|
||||
}
|
||||
|
||||
func executeListRules(cmd *base.Command, args []string) {
|
||||
setSharedFlags(cmd)
|
||||
cmd.Flag.Parse(args)
|
||||
|
||||
conn, ctx, close := dialAPIServer()
|
||||
defer close()
|
||||
|
||||
client := routerService.NewRoutingServiceClient(conn)
|
||||
resp, err := client.ListRule(ctx, &routerService.ListRuleRequest{})
|
||||
if err != nil {
|
||||
base.Fatalf("failed to list rules: %s", err)
|
||||
}
|
||||
showJSONResponse(resp)
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/xtls/xray-core/main/commands/base"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
|
||||
var cmdCertChainHash = &base.Command{
|
||||
UsageLine: "{{.Exec}} certChainHash",
|
||||
Short: "Calculate TLS certificates hash.",
|
||||
Long: `
|
||||
xray tls certChainHash --cert <cert.pem>
|
||||
Calculate TLS certificate chain hash.
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdCertChainHash.Run = executeCertChainHash // break init loop
|
||||
}
|
||||
|
||||
var input = cmdCertChainHash.Flag.String("cert", "fullchain.pem", "The file path of the certificates chain")
|
||||
|
||||
func executeCertChainHash(cmd *base.Command, args []string) {
|
||||
fs := flag.NewFlagSet("certChainHash", flag.ContinueOnError)
|
||||
if err := fs.Parse(args); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
certContent, err := os.ReadFile(*input)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
certChainHashB64 := tls.CalculatePEMCertChainSHA256Hash(certContent)
|
||||
fmt.Println(certChainHashB64)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/xtls/xray-core/main/commands/base"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
|
||||
var cmdLeafCertHash = &base.Command{
|
||||
UsageLine: "{{.Exec}} tls leafCertHash",
|
||||
Short: "Calculate TLS leaf certificate hash.",
|
||||
Long: `
|
||||
xray tls leafCertHash --cert <cert.pem>
|
||||
Calculate TLS leaf certificate hash.
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdLeafCertHash.Run = executeLeafCertHash // break init loop
|
||||
}
|
||||
|
||||
var input = cmdLeafCertHash.Flag.String("cert", "fullchain.pem", "The file path of the leaf certificate")
|
||||
|
||||
func executeLeafCertHash(cmd *base.Command, args []string) {
|
||||
fs := flag.NewFlagSet("leafCertHash", flag.ContinueOnError)
|
||||
if err := fs.Parse(args); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
certContent, err := os.ReadFile(*input)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
certChainHashB64, err := tls.CalculatePEMLeafCertSHA256Hash(certContent)
|
||||
if err != nil {
|
||||
fmt.Println("failed to decode cert", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(certChainHashB64)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package tls
|
||||
import (
|
||||
gotls "crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
@@ -156,8 +156,14 @@ func printTLSConnDetail(tlsConn *gotls.Conn) {
|
||||
|
||||
func showCert() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
hash := GenerateCertChainHash(rawCerts)
|
||||
fmt.Println("Certificate Chain Hash: ", base64.StdEncoding.EncodeToString(hash))
|
||||
var hash []byte
|
||||
for _, asn1Data := range rawCerts {
|
||||
cert, _ := x509.ParseCertificate(asn1Data)
|
||||
if cert.IsCA {
|
||||
hash = GenerateCertHash(cert)
|
||||
}
|
||||
}
|
||||
fmt.Println("Certificate Leaf Hash: ", hex.EncodeToString(hash))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ var CmdTLS = &base.Command{
|
||||
Commands: []*base.Command{
|
||||
cmdCert,
|
||||
cmdPing,
|
||||
cmdCertChainHash,
|
||||
cmdLeafCertHash,
|
||||
cmdECH,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
package hysteria
|
||||
|
||||
import (
|
||||
"context"
|
||||
go_errors "errors"
|
||||
"io"
|
||||
"math/rand"
|
||||
|
||||
"github.com/apernet/quic-go"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
hyCtx "github.com/xtls/xray-core/proxy/hysteria/ctx"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
server *protocol.ServerSpec
|
||||
policyManager policy.Manager
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
|
||||
if config.Server == nil {
|
||||
return nil, errors.New(`no target server found`)
|
||||
}
|
||||
server, err := protocol.NewServerSpecFromPB(config.Server)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to get server spec").Base(err)
|
||||
}
|
||||
|
||||
v := core.MustFromContext(ctx)
|
||||
client := &Client{
|
||||
server: server,
|
||||
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
if !ob.Target.IsValid() {
|
||||
return errors.New("target not specified")
|
||||
}
|
||||
ob.Name = "hysteria"
|
||||
ob.CanSpliceCopy = 3
|
||||
target := ob.Target
|
||||
|
||||
conn, err := dialer.Dial(hyCtx.ContextWithRequireDatagram(ctx, target.Network == net.Network_UDP), c.server.Destination)
|
||||
if err != nil {
|
||||
return errors.New("failed to find an available destination").AtWarning().Base(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
errors.LogInfo(ctx, "tunneling request to ", target, " via ", target.Network, ":", c.server.Destination.NetAddr())
|
||||
|
||||
var newCtx context.Context
|
||||
var newCancel context.CancelFunc
|
||||
if session.TimeoutOnlyFromContext(ctx) {
|
||||
newCtx, newCancel = context.WithCancel(context.Background())
|
||||
}
|
||||
|
||||
sessionPolicy := c.policyManager.ForLevel(0)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
timer := signal.CancelAfterInactivity(ctx, func() {
|
||||
cancel()
|
||||
if newCancel != nil {
|
||||
newCancel()
|
||||
}
|
||||
}, sessionPolicy.Timeouts.ConnectionIdle)
|
||||
|
||||
if newCtx != nil {
|
||||
ctx = newCtx
|
||||
}
|
||||
|
||||
if target.Network == net.Network_TCP {
|
||||
requestDone := func() error {
|
||||
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
||||
bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
|
||||
err := WriteTCPRequest(bufferedWriter, target.NetAddr())
|
||||
if err != nil {
|
||||
return errors.New("failed to write request").Base(err)
|
||||
}
|
||||
if err := bufferedWriter.SetBuffered(false); err != nil {
|
||||
return err
|
||||
}
|
||||
return buf.Copy(link.Reader, bufferedWriter, buf.UpdateActivity(timer))
|
||||
}
|
||||
|
||||
responseDone := func() error {
|
||||
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
|
||||
ok, msg, err := ReadTCPResponse(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return errors.New(msg)
|
||||
}
|
||||
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
|
||||
}
|
||||
|
||||
responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer))
|
||||
if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil {
|
||||
return errors.New("connection ends").Base(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if target.Network == net.Network_UDP {
|
||||
iConn := stat.TryUnwrapStatsConn(conn)
|
||||
_, ok := iConn.(*hysteria.InterUdpConn)
|
||||
if !ok {
|
||||
return errors.New("udp requires hysteria udp transport")
|
||||
}
|
||||
|
||||
requestDone := func() error {
|
||||
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
||||
|
||||
writer := &UDPWriter{
|
||||
Writer: conn,
|
||||
buf: make([]byte, MaxUDPSize),
|
||||
addr: target.NetAddr(),
|
||||
}
|
||||
|
||||
if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil {
|
||||
return errors.New("failed to transport all UDP request").Base(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
responseDone := func() error {
|
||||
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
|
||||
|
||||
reader := &UDPReader{
|
||||
Reader: conn,
|
||||
df: &Defragger{},
|
||||
}
|
||||
|
||||
if err := buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)); err != nil {
|
||||
return errors.New("failed to transport all UDP response").Base(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer))
|
||||
if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil {
|
||||
return errors.New("connection ends").Base(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return NewClient(ctx, config.(*ClientConfig))
|
||||
}))
|
||||
}
|
||||
|
||||
type UDPWriter struct {
|
||||
Writer io.Writer
|
||||
buf []byte
|
||||
addr string
|
||||
}
|
||||
|
||||
func (w *UDPWriter) sendMsg(msg *UDPMessage) error {
|
||||
msgN := msg.Serialize(w.buf)
|
||||
if msgN < 0 {
|
||||
// Message larger than buffer, silent drop
|
||||
return nil
|
||||
}
|
||||
_, err := w.Writer.Write(w.buf[:msgN])
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *UDPWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
for {
|
||||
mb2, b := buf.SplitFirst(mb)
|
||||
mb = mb2
|
||||
if b == nil {
|
||||
break
|
||||
}
|
||||
addr := w.addr
|
||||
if b.UDP != nil {
|
||||
addr = b.UDP.NetAddr()
|
||||
}
|
||||
msg := &UDPMessage{
|
||||
SessionID: 0,
|
||||
PacketID: 0,
|
||||
FragID: 0,
|
||||
FragCount: 1,
|
||||
Addr: addr,
|
||||
Data: b.Bytes(),
|
||||
}
|
||||
if err := w.sendMsg(msg); err != nil {
|
||||
var errTooLarge *quic.DatagramTooLargeError
|
||||
if go_errors.As(err, &errTooLarge) {
|
||||
msg.PacketID = uint16(rand.Intn(0xFFFF)) + 1
|
||||
fMsgs := FragUDPMessage(msg, int(errTooLarge.MaxDatagramPayloadSize))
|
||||
for _, fMsg := range fMsgs {
|
||||
err := w.sendMsg(&fMsg)
|
||||
if err != nil {
|
||||
b.Release()
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
b.Release()
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.Release()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UDPReader struct {
|
||||
Reader io.Reader
|
||||
df *Defragger
|
||||
}
|
||||
|
||||
func (r *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
for {
|
||||
b := buf.New()
|
||||
_, err := b.ReadFrom(r.Reader)
|
||||
if err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg, err := ParseUDPMessage(b.Bytes())
|
||||
if err != nil {
|
||||
b.Release()
|
||||
continue
|
||||
}
|
||||
|
||||
dfMsg := r.df.Feed(msg)
|
||||
if dfMsg == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
dest, _ := net.ParseDestination("udp:" + dfMsg.Addr)
|
||||
|
||||
buffer := buf.New()
|
||||
buffer.Write(dfMsg.Data)
|
||||
buffer.UDP = &dest
|
||||
|
||||
return buf.MultiBuffer{buffer}, nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package hysteria
|
||||
|
||||
import (
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/padding"
|
||||
)
|
||||
|
||||
var (
|
||||
tcpRequestPadding = padding.Padding{Min: 64, Max: 512}
|
||||
// tcpResponsePadding = padding.Padding{Min: 128, Max: 1024}
|
||||
)
|
||||
@@ -0,0 +1,135 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.10
|
||||
// protoc v6.33.1
|
||||
// source: proxy/hysteria/config.proto
|
||||
|
||||
package hysteria
|
||||
|
||||
import (
|
||||
protocol "github.com/xtls/xray-core/common/protocol"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ClientConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
|
||||
Server *protocol.ServerEndpoint `protobuf:"bytes,2,opt,name=server,proto3" json:"server,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClientConfig) Reset() {
|
||||
*x = ClientConfig{}
|
||||
mi := &file_proxy_hysteria_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ClientConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ClientConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_hysteria_config_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead.
|
||||
func (*ClientConfig) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_hysteria_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetVersion() int32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetServer() *protocol.ServerEndpoint {
|
||||
if x != nil {
|
||||
return x.Server
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_proxy_hysteria_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_proxy_hysteria_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x1bproxy/hysteria/config.proto\x12\x13xray.proxy.hysteria\x1a!common/protocol/server_spec.proto\"f\n" +
|
||||
"\fClientConfig\x12\x18\n" +
|
||||
"\aversion\x18\x01 \x01(\x05R\aversion\x12<\n" +
|
||||
"\x06server\x18\x02 \x01(\v2$.xray.common.protocol.ServerEndpointR\x06serverB[\n" +
|
||||
"\x17com.xray.proxy.hysteriaP\x01Z(github.com/xtls/xray-core/proxy/hysteria\xaa\x02\x13Xray.Proxy.Hysteriab\x06proto3"
|
||||
|
||||
var (
|
||||
file_proxy_hysteria_config_proto_rawDescOnce sync.Once
|
||||
file_proxy_hysteria_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_proxy_hysteria_config_proto_rawDescGZIP() []byte {
|
||||
file_proxy_hysteria_config_proto_rawDescOnce.Do(func() {
|
||||
file_proxy_hysteria_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_hysteria_config_proto_rawDesc), len(file_proxy_hysteria_config_proto_rawDesc)))
|
||||
})
|
||||
return file_proxy_hysteria_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proxy_hysteria_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_proxy_hysteria_config_proto_goTypes = []any{
|
||||
(*ClientConfig)(nil), // 0: xray.proxy.hysteria.ClientConfig
|
||||
(*protocol.ServerEndpoint)(nil), // 1: xray.common.protocol.ServerEndpoint
|
||||
}
|
||||
var file_proxy_hysteria_config_proto_depIdxs = []int32{
|
||||
1, // 0: xray.proxy.hysteria.ClientConfig.server:type_name -> xray.common.protocol.ServerEndpoint
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proxy_hysteria_config_proto_init() }
|
||||
func file_proxy_hysteria_config_proto_init() {
|
||||
if File_proxy_hysteria_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_hysteria_config_proto_rawDesc), len(file_proxy_hysteria_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_proxy_hysteria_config_proto_goTypes,
|
||||
DependencyIndexes: file_proxy_hysteria_config_proto_depIdxs,
|
||||
MessageInfos: file_proxy_hysteria_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proxy_hysteria_config_proto = out.File
|
||||
file_proxy_hysteria_config_proto_goTypes = nil
|
||||
file_proxy_hysteria_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.proxy.hysteria;
|
||||
option csharp_namespace = "Xray.Proxy.Hysteria";
|
||||
option go_package = "github.com/xtls/xray-core/proxy/hysteria";
|
||||
option java_package = "com.xray.proxy.hysteria";
|
||||
option java_multiple_files = true;
|
||||
|
||||
import "common/protocol/server_spec.proto";
|
||||
|
||||
message ClientConfig {
|
||||
int32 version = 1;
|
||||
xray.common.protocol.ServerEndpoint server = 2;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type key int
|
||||
|
||||
const (
|
||||
requireDatagram key = iota
|
||||
)
|
||||
|
||||
func ContextWithRequireDatagram(ctx context.Context, udp bool) context.Context {
|
||||
if !udp {
|
||||
return ctx
|
||||
}
|
||||
return context.WithValue(ctx, requireDatagram, struct{}{})
|
||||
}
|
||||
|
||||
func RequireDatagramFromContext(ctx context.Context) bool {
|
||||
_, ok := ctx.Value(requireDatagram).(struct{})
|
||||
return ok
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package hysteria
|
||||
|
||||
func FragUDPMessage(m *UDPMessage, maxSize int) []UDPMessage {
|
||||
if m.Size() <= maxSize {
|
||||
return []UDPMessage{*m}
|
||||
}
|
||||
fullPayload := m.Data
|
||||
maxPayloadSize := maxSize - m.HeaderSize()
|
||||
off := 0
|
||||
fragID := uint8(0)
|
||||
fragCount := uint8((len(fullPayload) + maxPayloadSize - 1) / maxPayloadSize) // round up
|
||||
frags := make([]UDPMessage, fragCount)
|
||||
for off < len(fullPayload) {
|
||||
payloadSize := len(fullPayload) - off
|
||||
if payloadSize > maxPayloadSize {
|
||||
payloadSize = maxPayloadSize
|
||||
}
|
||||
frag := *m
|
||||
frag.FragID = fragID
|
||||
frag.FragCount = fragCount
|
||||
frag.Data = fullPayload[off : off+payloadSize]
|
||||
frags[fragID] = frag
|
||||
off += payloadSize
|
||||
fragID++
|
||||
}
|
||||
return frags
|
||||
}
|
||||
|
||||
// Defragger handles the defragmentation of UDP messages.
|
||||
// The current implementation can only handle one packet ID at a time.
|
||||
// If another packet arrives before a packet has received all fragments
|
||||
// in their entirety, any previous state is discarded.
|
||||
type Defragger struct {
|
||||
pktID uint16
|
||||
frags []*UDPMessage
|
||||
count uint8
|
||||
size int // data size
|
||||
}
|
||||
|
||||
func (d *Defragger) Feed(m *UDPMessage) *UDPMessage {
|
||||
if m.FragCount <= 1 {
|
||||
return m
|
||||
}
|
||||
if m.FragID >= m.FragCount {
|
||||
// wtf is this?
|
||||
return nil
|
||||
}
|
||||
if m.PacketID != d.pktID || m.FragCount != uint8(len(d.frags)) {
|
||||
// new message, clear previous state
|
||||
d.pktID = m.PacketID
|
||||
d.frags = make([]*UDPMessage, m.FragCount)
|
||||
d.frags[m.FragID] = m
|
||||
d.count = 1
|
||||
d.size = len(m.Data)
|
||||
} else if d.frags[m.FragID] == nil {
|
||||
d.frags[m.FragID] = m
|
||||
d.count++
|
||||
d.size += len(m.Data)
|
||||
if int(d.count) == len(d.frags) {
|
||||
// all fragments received, assemble
|
||||
data := make([]byte, d.size)
|
||||
off := 0
|
||||
for _, frag := range d.frags {
|
||||
off += copy(data[off:], frag.Data)
|
||||
}
|
||||
m.Data = data
|
||||
m.FragID = 0
|
||||
m.FragCount = 1
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package hysteria
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/apernet/quic-go/quicvarint"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
FrameTypeTCPRequest = 0x401
|
||||
|
||||
// Max length values are for preventing DoS attacks
|
||||
|
||||
MaxAddressLength = 2048
|
||||
MaxMessageLength = 2048
|
||||
MaxPaddingLength = 4096
|
||||
|
||||
MaxUDPSize = 4096
|
||||
|
||||
maxVarInt1 = 63
|
||||
maxVarInt2 = 16383
|
||||
maxVarInt4 = 1073741823
|
||||
maxVarInt8 = 4611686018427387903
|
||||
)
|
||||
|
||||
// TCPRequest format:
|
||||
// 0x401 (QUIC varint)
|
||||
// Address length (QUIC varint)
|
||||
// Address (bytes)
|
||||
// Padding length (QUIC varint)
|
||||
// Padding (bytes)
|
||||
|
||||
func WriteTCPRequest(w io.Writer, addr string) error {
|
||||
padding := tcpRequestPadding.String()
|
||||
paddingLen := len(padding)
|
||||
addrLen := len(addr)
|
||||
sz := int(quicvarint.Len(FrameTypeTCPRequest)) +
|
||||
int(quicvarint.Len(uint64(addrLen))) + addrLen +
|
||||
int(quicvarint.Len(uint64(paddingLen))) + paddingLen
|
||||
buf := make([]byte, sz)
|
||||
i := varintPut(buf, FrameTypeTCPRequest)
|
||||
i += varintPut(buf[i:], uint64(addrLen))
|
||||
i += copy(buf[i:], addr)
|
||||
i += varintPut(buf[i:], uint64(paddingLen))
|
||||
copy(buf[i:], padding)
|
||||
_, err := w.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
// TCPResponse format:
|
||||
// Status (byte, 0=ok, 1=error)
|
||||
// Message length (QUIC varint)
|
||||
// Message (bytes)
|
||||
// Padding length (QUIC varint)
|
||||
// Padding (bytes)
|
||||
|
||||
func ReadTCPResponse(r io.Reader) (bool, string, error) {
|
||||
var status [1]byte
|
||||
if _, err := io.ReadFull(r, status[:]); err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
bReader := quicvarint.NewReader(r)
|
||||
msgLen, err := quicvarint.Read(bReader)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if msgLen > MaxMessageLength {
|
||||
return false, "", errors.New("invalid message length")
|
||||
}
|
||||
var msgBuf []byte
|
||||
// No message is fine
|
||||
if msgLen > 0 {
|
||||
msgBuf = make([]byte, msgLen)
|
||||
_, err = io.ReadFull(r, msgBuf)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
}
|
||||
paddingLen, err := quicvarint.Read(bReader)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if paddingLen > MaxPaddingLength {
|
||||
return false, "", errors.New("invalid padding length")
|
||||
}
|
||||
if paddingLen > 0 {
|
||||
_, err = io.CopyN(io.Discard, r, int64(paddingLen))
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
}
|
||||
return status[0] == 0, string(msgBuf), nil
|
||||
}
|
||||
|
||||
// UDPMessage format:
|
||||
// Session ID (uint32 BE)
|
||||
// Packet ID (uint16 BE)
|
||||
// Fragment ID (uint8)
|
||||
// Fragment count (uint8)
|
||||
// Address length (QUIC varint)
|
||||
// Address (bytes)
|
||||
// Data...
|
||||
|
||||
type UDPMessage struct {
|
||||
SessionID uint32 // 4
|
||||
PacketID uint16 // 2
|
||||
FragID uint8 // 1
|
||||
FragCount uint8 // 1
|
||||
Addr string // varint + bytes
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (m *UDPMessage) HeaderSize() int {
|
||||
lAddr := len(m.Addr)
|
||||
return 4 + 2 + 1 + 1 + int(quicvarint.Len(uint64(lAddr))) + lAddr
|
||||
}
|
||||
|
||||
func (m *UDPMessage) Size() int {
|
||||
return m.HeaderSize() + len(m.Data)
|
||||
}
|
||||
|
||||
func (m *UDPMessage) Serialize(buf []byte) int {
|
||||
// Make sure the buffer is big enough
|
||||
if len(buf) < m.Size() {
|
||||
return -1
|
||||
}
|
||||
// binary.BigEndian.PutUint32(buf, m.SessionID)
|
||||
binary.BigEndian.PutUint16(buf[4:], m.PacketID)
|
||||
buf[6] = m.FragID
|
||||
buf[7] = m.FragCount
|
||||
i := varintPut(buf[8:], uint64(len(m.Addr)))
|
||||
i += copy(buf[8+i:], m.Addr)
|
||||
i += copy(buf[8+i:], m.Data)
|
||||
return 8 + i
|
||||
}
|
||||
|
||||
func ParseUDPMessage(msg []byte) (*UDPMessage, error) {
|
||||
m := &UDPMessage{}
|
||||
buf := bytes.NewBuffer(msg)
|
||||
if err := binary.Read(buf, binary.BigEndian, &m.SessionID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(buf, binary.BigEndian, &m.PacketID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(buf, binary.BigEndian, &m.FragID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(buf, binary.BigEndian, &m.FragCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lAddr, err := quicvarint.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lAddr == 0 || lAddr > MaxMessageLength {
|
||||
return nil, errors.New("invalid address length")
|
||||
}
|
||||
bs := buf.Bytes()
|
||||
if len(bs) <= int(lAddr) {
|
||||
// We use <= instead of < here as we expect at least one byte of data after the address
|
||||
return nil, errors.New("invalid message length")
|
||||
}
|
||||
m.Addr = string(bs[:lAddr])
|
||||
m.Data = bs[lAddr:]
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// varintPut is like quicvarint.Append, but instead of appending to a slice,
|
||||
// it writes to a fixed-size buffer. Returns the number of bytes written.
|
||||
func varintPut(b []byte, i uint64) int {
|
||||
if i <= maxVarInt1 {
|
||||
b[0] = uint8(i)
|
||||
return 1
|
||||
}
|
||||
if i <= maxVarInt2 {
|
||||
b[0] = uint8(i>>8) | 0x40
|
||||
b[1] = uint8(i)
|
||||
return 2
|
||||
}
|
||||
if i <= maxVarInt4 {
|
||||
b[0] = uint8(i>>24) | 0x80
|
||||
b[1] = uint8(i >> 16)
|
||||
b[2] = uint8(i >> 8)
|
||||
b[3] = uint8(i)
|
||||
return 4
|
||||
}
|
||||
if i <= maxVarInt8 {
|
||||
b[0] = uint8(i>>56) | 0xc0
|
||||
b[1] = uint8(i >> 48)
|
||||
b[2] = uint8(i >> 40)
|
||||
b[3] = uint8(i >> 32)
|
||||
b[4] = uint8(i >> 24)
|
||||
b[5] = uint8(i >> 16)
|
||||
b[6] = uint8(i >> 8)
|
||||
b[7] = uint8(i)
|
||||
return 8
|
||||
}
|
||||
panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i))
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"crypto/cipher"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"io"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/antireplay"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -24,8 +24,6 @@ type MemoryAccount struct {
|
||||
CipherType CipherType
|
||||
Key []byte
|
||||
Password string
|
||||
|
||||
replayFilter antireplay.GeneralizedReplayFilter
|
||||
}
|
||||
|
||||
var ErrIVNotUnique = errors.New("IV is not unique")
|
||||
@@ -42,20 +40,9 @@ func (a *MemoryAccount) ToProto() proto.Message {
|
||||
return &Account{
|
||||
CipherType: a.CipherType,
|
||||
Password: a.Password,
|
||||
IvCheck: a.replayFilter != nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *MemoryAccount) CheckIV(iv []byte) error {
|
||||
if a.replayFilter == nil {
|
||||
return nil
|
||||
}
|
||||
if a.replayFilter.Check(iv) {
|
||||
return nil
|
||||
}
|
||||
return ErrIVNotUnique
|
||||
}
|
||||
|
||||
func createAesGcm(key []byte) cipher.AEAD {
|
||||
return crypto.NewAesGcm(key)
|
||||
}
|
||||
@@ -116,12 +103,6 @@ func (a *Account) AsAccount() (protocol.Account, error) {
|
||||
CipherType: a.CipherType,
|
||||
Key: passwordToCipherKey([]byte(a.Password), Cipher.KeySize()),
|
||||
Password: a.Password,
|
||||
replayFilter: func() antireplay.GeneralizedReplayFilter {
|
||||
if a.IvCheck {
|
||||
return antireplay.NewBloomRing()
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -139,9 +139,6 @@ func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (buf.Wri
|
||||
if account.Cipher.IVSize() > 0 {
|
||||
iv = make([]byte, account.Cipher.IVSize())
|
||||
common.Must2(rand.Read(iv))
|
||||
if ivError := account.CheckIV(iv); ivError != nil {
|
||||
return nil, errors.New("failed to mark outgoing iv").Base(ivError)
|
||||
}
|
||||
if err := buf.WriteAllBytes(writer, iv, nil); err != nil {
|
||||
return nil, errors.New("failed to write IV")
|
||||
}
|
||||
@@ -188,10 +185,6 @@ func ReadTCPResponse(user *protocol.MemoryUser, reader io.Reader) (buf.Reader, e
|
||||
}
|
||||
}
|
||||
|
||||
if ivError := account.CheckIV(iv); ivError != nil {
|
||||
return nil, drain.WithError(drainer, reader, errors.New("failed iv check").Base(ivError))
|
||||
}
|
||||
|
||||
return account.Cipher.NewDecryptionReader(account.Key, iv, reader)
|
||||
}
|
||||
|
||||
@@ -203,9 +196,6 @@ func WriteTCPResponse(request *protocol.RequestHeader, writer io.Writer) (buf.Wr
|
||||
if account.Cipher.IVSize() > 0 {
|
||||
iv = make([]byte, account.Cipher.IVSize())
|
||||
common.Must2(rand.Read(iv))
|
||||
if ivError := account.CheckIV(iv); ivError != nil {
|
||||
return nil, errors.New("failed to mark outgoing iv").Base(ivError)
|
||||
}
|
||||
if err := buf.WriteAllBytes(writer, iv, nil); err != nil {
|
||||
return nil, errors.New("failed to write IV.").Base(err)
|
||||
}
|
||||
|
||||
@@ -140,7 +140,6 @@ func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol
|
||||
|
||||
if matchErr == nil {
|
||||
u = user
|
||||
err = account.CheckIV(iv)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
+59
-1
@@ -4,7 +4,7 @@ TUN interface support bridges the gap between network layer 3 and layer 7, intro
|
||||
|
||||
This functionality is targeted to assist applications/end devices that don't have proxy support, or can't run external applications (like Smart TV's). Making it possible to run Xray proxy right on network edge devices (routers) with support to route raw network traffic. \
|
||||
Primary targets are Linux based router devices. Like most popular OpenWRT option. \
|
||||
Although support for Windows is also implemented (see below).
|
||||
Support for Windows, macOS, Android and iOS is also implemented (see below).
|
||||
|
||||
## PLEASE READ FOLLOWING CAREFULLY
|
||||
|
||||
@@ -172,3 +172,61 @@ route add 1.1.1.1 mask 255.0.0.0 0.0.0.0 if 47
|
||||
Note on ipv6 support. \
|
||||
Despite Windows also giving the adapter autoconfigured ipv6 address, the ipv6 is not possible until the interface has any _routable_ ipv6 address (given link-local address will not accept traffic from external addresses). \
|
||||
So everything applicable for ipv4 above also works for ipv6, you only need to give the interface some address manually, e.g. anything private like fc00::a:b:c:d/64 will do just fine
|
||||
|
||||
## MAC OS X SUPPORT
|
||||
|
||||
Darwin (Mac OS X) support of the same functionality is implemented through utun (userspace tunnel).
|
||||
|
||||
Interface name in the configuration must comply to the scheme "utunN", where N is some number. \
|
||||
Most running OS'es create some amount of utun interfaces in advance for own needs. Please either check the interfaces you already have occupied by issuing following command:
|
||||
```
|
||||
ifconfig
|
||||
```
|
||||
Produced list will have all system interfaces listed, from which you will see how many "utun" ones already exists.
|
||||
It's not required to select next available number, e.g. if you have utun1-utun7 interfaces, it's not required to have "utun8" in the config. You can choose any available name, even utun20, to get surely available interface number.
|
||||
|
||||
To attach routing to the interface, route command like following can be executed:
|
||||
```
|
||||
sudo route add -net 1.1.1.0/24 -iface utun10
|
||||
```
|
||||
```
|
||||
sudo route add -inet6 -host 2606:4700:4700::1111 -iface utun10
|
||||
sudo route add -inet6 -host 2606:4700:4700::1001 -iface utun10
|
||||
```
|
||||
Important to remember that everything written above about Linux routing concept, also apply to Mac OS X. If you simply route default route through utun interface, that will result network loop and immediate network failure.
|
||||
|
||||
## ANDROID SUPPORT
|
||||
|
||||
Android uses the VpnService API which provides a TUN file descriptor to the application.
|
||||
|
||||
Obtain the fd from VpnService:
|
||||
```kotlin
|
||||
val tunFd = vpnInterface.fd
|
||||
```
|
||||
|
||||
Set the environment variable `xray.tun.fd` (or `XRAY_TUN_FD`) to the fd number before starting Xray. This can be done from Kotlin/Java or by exposing a Go function via gomobile bindings.
|
||||
|
||||
Build using gomobile for Android library integration:
|
||||
```
|
||||
gomobile bind -target=android
|
||||
```
|
||||
|
||||
## iOS SUPPORT
|
||||
|
||||
iOS uses the same utun packet format as macOS, but the file descriptor is provided by NetworkExtension.
|
||||
|
||||
Obtain the fd from NetworkExtension:
|
||||
```swift
|
||||
var buf = [CChar](repeating: 0, count: Int(IFNAMSIZ))
|
||||
let utunPrefix = "utun".utf8CString.dropLast()
|
||||
let tunFd = ((0 ... 1024).first { (_ fd: Int32) -> Bool in var len = socklen_t(buf.count)
|
||||
return getsockopt(fd, 2, 2, &buf, &len) == 0 && buf.starts(with: utunPrefix)
|
||||
}!
|
||||
```
|
||||
|
||||
Set the environment variable `xray.tun.fd` (or `XRAY_TUN_FD`) to the fd number before starting Xray. This can be done from Swift/Objective-C or by exposing a Go function via gomobile bindings.
|
||||
|
||||
Build using gomobile for iOS framework integration:
|
||||
```
|
||||
gomobile bind -target=ios
|
||||
```
|
||||
+43
-17
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
c "github.com/xtls/xray-core/common/ctx"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
@@ -19,11 +20,13 @@ import (
|
||||
|
||||
// Handler is managing object that tie together tun interface, ip stack and dispatch connections to the routing
|
||||
type Handler struct {
|
||||
ctx context.Context
|
||||
config *Config
|
||||
stack Stack
|
||||
policyManager policy.Manager
|
||||
dispatcher routing.Dispatcher
|
||||
ctx context.Context
|
||||
config *Config
|
||||
stack Stack
|
||||
policyManager policy.Manager
|
||||
dispatcher routing.Dispatcher
|
||||
tag string
|
||||
sniffingRequest session.SniffingRequest
|
||||
}
|
||||
|
||||
// ConnectionHandler interface with the only method that stack is going to push new connections to
|
||||
@@ -43,6 +46,14 @@ func (t *Handler) policy() policy.Session {
|
||||
func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routing.Dispatcher) error {
|
||||
var err error
|
||||
|
||||
// Retrieve tag and sniffing config from context (set by AlwaysOnInboundHandler)
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||
t.tag = inbound.Tag
|
||||
}
|
||||
if content := session.ContentFromContext(ctx); content != nil {
|
||||
t.sniffingRequest = content.SniffingRequest
|
||||
}
|
||||
|
||||
t.ctx = core.ToBackgroundDetachedContext(ctx)
|
||||
t.policyManager = pm
|
||||
t.dispatcher = dispatcher
|
||||
@@ -91,31 +102,46 @@ func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routin
|
||||
|
||||
// HandleConnection pass the connection coming from the ip stack to the routing dispatcher
|
||||
func (t *Handler) HandleConnection(conn net.Conn, destination net.Destination) {
|
||||
sid := session.NewID()
|
||||
ctx := c.ContextWithID(t.ctx, sid)
|
||||
errors.LogInfo(ctx, "processing connection from: ", conn.RemoteAddr())
|
||||
// when handling is done with any outcome, always signal back to the incoming connection
|
||||
// to close, send completion packets back to the network, and cleanup
|
||||
defer conn.Close()
|
||||
|
||||
inbound := session.Inbound{}
|
||||
inbound.Name = "tun"
|
||||
inbound.CanSpliceCopy = 1
|
||||
inbound.Source = net.DestinationFromAddr(conn.RemoteAddr())
|
||||
inbound.User = &protocol.MemoryUser{
|
||||
Level: t.config.UserLevel,
|
||||
ctx, cancel := context.WithCancel(t.ctx)
|
||||
defer cancel()
|
||||
ctx = c.ContextWithID(ctx, session.NewID())
|
||||
|
||||
source := net.DestinationFromAddr(conn.RemoteAddr())
|
||||
inbound := session.Inbound{
|
||||
Name: "tun",
|
||||
Tag: t.tag,
|
||||
CanSpliceCopy: 3,
|
||||
Source: source,
|
||||
User: &protocol.MemoryUser{
|
||||
Level: t.config.UserLevel,
|
||||
},
|
||||
}
|
||||
|
||||
ctx = session.ContextWithInbound(ctx, &inbound)
|
||||
ctx = session.ContextWithContent(ctx, &session.Content{
|
||||
SniffingRequest: t.sniffingRequest,
|
||||
})
|
||||
ctx = session.SubContextFromMuxInbound(ctx)
|
||||
|
||||
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||
From: inbound.Source,
|
||||
To: destination,
|
||||
Status: log.AccessAccepted,
|
||||
Reason: "",
|
||||
})
|
||||
errors.LogInfo(ctx, "processing from ", source, " to ", destination)
|
||||
|
||||
link := &transport.Link{
|
||||
Reader: &buf.TimeoutWrapperReader{Reader: buf.NewReader(conn)},
|
||||
Writer: buf.NewWriter(conn),
|
||||
}
|
||||
if err := t.dispatcher.DispatchLink(ctx, destination, link); err != nil {
|
||||
errors.LogError(ctx, errors.New("connection closed").Base(err))
|
||||
return
|
||||
}
|
||||
|
||||
errors.LogInfo(ctx, "connection completed")
|
||||
}
|
||||
|
||||
// Network implements proxy.Inbound
|
||||
|
||||
+82
-24
@@ -6,8 +6,10 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
@@ -100,32 +102,21 @@ func (t *stackGVisor) Start() error {
|
||||
})
|
||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
||||
|
||||
udpForwarder := udp.NewForwarder(ipStack, func(r *udp.ForwarderRequest) {
|
||||
go func(r *udp.ForwarderRequest) {
|
||||
var wq waiter.Queue
|
||||
var id = r.ID()
|
||||
// Use custom UDP packet handler, instead of strict gVisor forwarder, for FullCone NAT support
|
||||
udpForwarder := newUdpConnectionHandler(t.handler.HandleConnection, t.writeRawUDPPacket)
|
||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||
data := pkt.Data().AsRange().ToSlice()
|
||||
if len(data) == 0 {
|
||||
return false
|
||||
}
|
||||
// source/destination of the packet we process as incoming, on gVisor side are Remote/Local
|
||||
// in other terms, src is the side behind tun, dst is the side behind gVisor
|
||||
// this function handle packets passing from the tun to the gVisor, therefore the src/dst assignement
|
||||
src := net.UDPDestination(net.IPAddress(id.RemoteAddress.AsSlice()), net.Port(id.RemotePort))
|
||||
dst := net.UDPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort))
|
||||
|
||||
ep, err := r.CreateEndpoint(&wq)
|
||||
if err != nil {
|
||||
errors.LogError(t.ctx, err.String())
|
||||
return
|
||||
}
|
||||
|
||||
options := ep.SocketOptions()
|
||||
options.SetReuseAddress(true)
|
||||
options.SetReusePort(true)
|
||||
|
||||
t.handler.HandleConnection(
|
||||
gonet.NewUDPConn(&wq, ep),
|
||||
// local address on the gVisor side is connection destination
|
||||
net.UDPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort)),
|
||||
)
|
||||
|
||||
// close the socket
|
||||
ep.Close()
|
||||
}(r)
|
||||
return udpForwarder.HandlePacket(src, dst, data)
|
||||
})
|
||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
||||
|
||||
t.stack = ipStack
|
||||
t.endpoint = linkEndpoint
|
||||
@@ -133,6 +124,69 @@ func (t *stackGVisor) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *stackGVisor) writeRawUDPPacket(payload []byte, src net.Destination, dst net.Destination) error {
|
||||
udpLen := header.UDPMinimumSize + len(payload)
|
||||
srcIP := tcpip.AddrFromSlice(src.Address.IP())
|
||||
dstIP := tcpip.AddrFromSlice(dst.Address.IP())
|
||||
|
||||
// build packet with appropriate IP header size
|
||||
isIPv4 := dst.Address.Family().IsIPv4()
|
||||
ipHdrSize := header.IPv6MinimumSize
|
||||
ipProtocol := header.IPv6ProtocolNumber
|
||||
if isIPv4 {
|
||||
ipHdrSize = header.IPv4MinimumSize
|
||||
ipProtocol = header.IPv4ProtocolNumber
|
||||
}
|
||||
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: ipHdrSize + header.UDPMinimumSize,
|
||||
Payload: buffer.MakeWithData(payload),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
|
||||
// Build UDP header
|
||||
udpHdr := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize))
|
||||
udpHdr.Encode(&header.UDPFields{
|
||||
SrcPort: uint16(src.Port),
|
||||
DstPort: uint16(dst.Port),
|
||||
Length: uint16(udpLen),
|
||||
})
|
||||
|
||||
// Calculate and set UDP checksum
|
||||
xsum := header.PseudoHeaderChecksum(header.UDPProtocolNumber, srcIP, dstIP, uint16(udpLen))
|
||||
udpHdr.SetChecksum(^udpHdr.CalculateChecksum(checksum.Checksum(payload, xsum)))
|
||||
|
||||
// Build IP header
|
||||
if isIPv4 {
|
||||
ipHdr := header.IPv4(pkt.NetworkHeader().Push(header.IPv4MinimumSize))
|
||||
ipHdr.Encode(&header.IPv4Fields{
|
||||
TotalLength: uint16(header.IPv4MinimumSize + udpLen),
|
||||
TTL: 64,
|
||||
Protocol: uint8(header.UDPProtocolNumber),
|
||||
SrcAddr: srcIP,
|
||||
DstAddr: dstIP,
|
||||
})
|
||||
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||
} else {
|
||||
ipHdr := header.IPv6(pkt.NetworkHeader().Push(header.IPv6MinimumSize))
|
||||
ipHdr.Encode(&header.IPv6Fields{
|
||||
PayloadLength: uint16(udpLen),
|
||||
TransportProtocol: header.UDPProtocolNumber,
|
||||
HopLimit: 64,
|
||||
SrcAddr: srcIP,
|
||||
DstAddr: dstIP,
|
||||
})
|
||||
}
|
||||
|
||||
// dispatch the packet
|
||||
err := t.stack.WriteRawPacket(defaultNIC, ipProtocol, buffer.MakeWithView(pkt.ToView()))
|
||||
if err != nil {
|
||||
return errors.New("failed to write raw udp packet back to stack", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close is called by Handler to shut down the stack
|
||||
func (t *stackGVisor) Close() error {
|
||||
if t.stack == nil {
|
||||
@@ -182,6 +236,10 @@ func createStack(ep stack.LinkEndpoint) (*stack.Stack, error) {
|
||||
mOpt := tcpip.TCPModerateReceiveBufferOption(true)
|
||||
gStack.SetTransportProtocolOption(tcp.ProtocolNumber, &mOpt)
|
||||
|
||||
// Disable RACK/TLP loss recovery to fix connection stalls under high load
|
||||
rOpt := tcpip.TCPRecovery(0)
|
||||
gStack.SetTransportProtocolOption(tcp.ProtocolNumber, &rOpt)
|
||||
|
||||
tcpRXBufOpt := tcpip.TCPReceiveBufferSizeRangeOption{
|
||||
Min: tcpRXBufMinSize,
|
||||
Default: tcpRXBufDefSize,
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
var ErrQueueEmpty = errors.New("queue is empty")
|
||||
|
||||
type GVisorDevice interface {
|
||||
WritePacket(packet *stack.PacketBuffer) tcpip.Error
|
||||
ReadPacket() (byte, *stack.PacketBuffer, error)
|
||||
Wait()
|
||||
}
|
||||
|
||||
// LinkEndpoint implements GVisor stack.LinkEndpoint
|
||||
var _ stack.LinkEndpoint = (*LinkEndpoint)(nil)
|
||||
|
||||
type LinkEndpoint struct {
|
||||
deviceMTU uint32
|
||||
device GVisorDevice
|
||||
dispatcherCancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) MTU() uint32 {
|
||||
return e.deviceMTU
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) SetMTU(_ uint32) {
|
||||
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) MaxHeaderLength() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) LinkAddress() tcpip.LinkAddress {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) SetLinkAddress(_ tcpip.LinkAddress) {
|
||||
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||
return stack.CapabilityRXChecksumOffload
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
if e.dispatcherCancel != nil {
|
||||
e.dispatcherCancel()
|
||||
e.dispatcherCancel = nil
|
||||
}
|
||||
|
||||
if dispatcher != nil {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go e.dispatchLoop(ctx, dispatcher)
|
||||
e.dispatcherCancel = cancel
|
||||
}
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) IsAttached() bool {
|
||||
return e.dispatcherCancel != nil
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) Wait() {
|
||||
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
return header.ARPHardwareNone
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) AddHeader(buffer *stack.PacketBuffer) {
|
||||
// tun interface doesn't have link layer header, it will be added by the OS
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) ParseHeader(ptr *stack.PacketBuffer) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) Close() {
|
||||
if e.dispatcherCancel != nil {
|
||||
e.dispatcherCancel()
|
||||
e.dispatcherCancel = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) SetOnCloseAction(_ func()) {
|
||||
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
||||
var n int
|
||||
var err tcpip.Error
|
||||
|
||||
for _, packetBuffer := range packetBufferList.AsSlice() {
|
||||
err = e.device.WritePacket(packetBuffer)
|
||||
if err != nil {
|
||||
return n, &tcpip.ErrAborted{}
|
||||
}
|
||||
n++
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (e *LinkEndpoint) dispatchLoop(ctx context.Context, dispatcher stack.NetworkDispatcher) {
|
||||
var networkProtocolNumber tcpip.NetworkProtocolNumber
|
||||
var version byte
|
||||
var packet *stack.PacketBuffer
|
||||
var err error
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
version, packet, err = e.device.ReadPacket()
|
||||
// on "queue empty", ask device to yield slightly and continue
|
||||
if errors.Is(err, ErrQueueEmpty) {
|
||||
e.device.Wait()
|
||||
continue
|
||||
}
|
||||
// stop dispatcher loop on any other interface failure
|
||||
if err != nil {
|
||||
e.Attach(nil)
|
||||
return
|
||||
}
|
||||
|
||||
// extract network protocol number from the packet first byte
|
||||
// (which is returned separately, since it is so incredibly hard to extract one byte from
|
||||
// stack.PacketBuffer without additional memory allocation and full copying it back and forth)
|
||||
switch version {
|
||||
case 4:
|
||||
networkProtocolNumber = header.IPv4ProtocolNumber
|
||||
case 6:
|
||||
networkProtocolNumber = header.IPv6ProtocolNumber
|
||||
default:
|
||||
// discard unknown network protocol packet
|
||||
packet.DecRef()
|
||||
continue
|
||||
}
|
||||
|
||||
// dispatch the buffer to the stack
|
||||
dispatcher.DeliverNetworkPacket(networkProtocolNumber, packet)
|
||||
// signal the buffer that it can be released
|
||||
packet.DecRef()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
//go:build darwin
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
const (
|
||||
utunControlName = "com.apple.net.utun_control"
|
||||
sysprotoControl = 2
|
||||
gateway = "169.254.10.1/30"
|
||||
utunHeaderSize = 4
|
||||
)
|
||||
|
||||
const (
|
||||
SIOCAIFADDR6 = 2155899162 // netinet6/in6_var.h
|
||||
IN6_IFF_NODAD = 0x0020 // netinet6/in6_var.h
|
||||
IN6_IFF_SECURED = 0x0400 // netinet6/in6_var.h
|
||||
ND6_INFINITE_LIFETIME = 0xFFFFFFFF // netinet6/nd6.h
|
||||
)
|
||||
|
||||
//go:linkname procyield runtime.procyield
|
||||
func procyield(cycles uint32)
|
||||
|
||||
type DarwinTun struct {
|
||||
tunFile *os.File
|
||||
options TunOptions
|
||||
ownsFd bool // true for macOS (we created the fd), false for iOS (fd from system)
|
||||
}
|
||||
|
||||
var _ Tun = (*DarwinTun)(nil)
|
||||
var _ GVisorTun = (*DarwinTun)(nil)
|
||||
var _ GVisorDevice = (*DarwinTun)(nil)
|
||||
|
||||
func NewTun(options TunOptions) (Tun, error) {
|
||||
// Check if fd is provided via environment (iOS mode)
|
||||
fdStr := platform.NewEnvFlag(platform.TunFdKey).GetValue(func() string { return "" })
|
||||
if fdStr != "" {
|
||||
// iOS: use provided fd from NetworkExtension
|
||||
fd, err := strconv.Atoi(fdStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = unix.SetNonblock(fd, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DarwinTun{
|
||||
tunFile: os.NewFile(uintptr(fd), "utun"),
|
||||
options: options,
|
||||
ownsFd: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// macOS: create our own utun interface
|
||||
tunFile, err := open(options.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = setup(options.Name, options.MTU)
|
||||
if err != nil {
|
||||
_ = tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DarwinTun{
|
||||
tunFile: tunFile,
|
||||
options: options,
|
||||
ownsFd: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *DarwinTun) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DarwinTun) Close() error {
|
||||
if t.ownsFd {
|
||||
return t.tunFile.Close()
|
||||
}
|
||||
// iOS: don't close the fd, it's owned by NetworkExtension
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePacket implements GVisorDevice method to write one packet to the tun device
|
||||
func (t *DarwinTun) WritePacket(packet *stack.PacketBuffer) tcpip.Error {
|
||||
// request memory to write from reusable buffer pool
|
||||
b := buf.NewWithSize(int32(t.options.MTU) + utunHeaderSize)
|
||||
defer b.Release()
|
||||
|
||||
// prepare Darwin specific packet header
|
||||
_, _ = b.Write([]byte{0x0, 0x0, 0x0, 0x0})
|
||||
// copy the bytes of slices that compose the packet into the allocated buffer
|
||||
for _, packetElement := range packet.AsSlices() {
|
||||
_, _ = b.Write(packetElement)
|
||||
}
|
||||
// fill Darwin specific header from the first raw packet byte, that we can access now
|
||||
var family byte
|
||||
switch b.Byte(4) >> 4 {
|
||||
case 4:
|
||||
family = unix.AF_INET
|
||||
case 6:
|
||||
family = unix.AF_INET6
|
||||
default:
|
||||
return &tcpip.ErrAborted{}
|
||||
}
|
||||
b.SetByte(3, family)
|
||||
|
||||
if _, err := t.tunFile.Write(b.Bytes()); err != nil {
|
||||
if errors.Is(err, unix.EAGAIN) {
|
||||
return &tcpip.ErrWouldBlock{}
|
||||
}
|
||||
return &tcpip.ErrAborted{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPacket implements GVisorDevice method to read one packet from the tun device
|
||||
// It is expected that the method will not block, rather return ErrQueueEmpty when there is nothing on the line,
|
||||
// which will make the stack call Wait which should implement desired push-back
|
||||
func (t *DarwinTun) ReadPacket() (byte, *stack.PacketBuffer, error) {
|
||||
// request memory to write from reusable buffer pool
|
||||
b := buf.NewWithSize(int32(t.options.MTU) + utunHeaderSize)
|
||||
|
||||
// read the bytes to the interface file
|
||||
n, err := b.ReadFrom(t.tunFile)
|
||||
if errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EINTR) {
|
||||
b.Release()
|
||||
return 0, nil, ErrQueueEmpty
|
||||
}
|
||||
if err != nil {
|
||||
b.Release()
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// discard empty or sub-empty packets
|
||||
if n <= utunHeaderSize {
|
||||
b.Release()
|
||||
return 0, nil, ErrQueueEmpty
|
||||
}
|
||||
|
||||
// network protocol version from first byte of the raw packet, the one that follows Darwin specific header
|
||||
version := b.Byte(utunHeaderSize) >> 4
|
||||
packetBuffer := buffer.MakeWithData(b.BytesFrom(utunHeaderSize))
|
||||
return version, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: packetBuffer,
|
||||
IsForwardedPacket: true,
|
||||
OnRelease: func() {
|
||||
b.Release()
|
||||
},
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Wait some cpu cycles
|
||||
func (t *DarwinTun) Wait() {
|
||||
procyield(1)
|
||||
}
|
||||
|
||||
func (t *DarwinTun) newEndpoint() (stack.LinkEndpoint, error) {
|
||||
return &LinkEndpoint{deviceMTU: t.options.MTU, device: t}, nil
|
||||
}
|
||||
|
||||
// open the interface, by creating new utunN if in the system and returning its file descriptor
|
||||
func open(name string) (*os.File, error) {
|
||||
ifIndex := -1
|
||||
_, err := fmt.Sscanf(name, "utun%d", &ifIndex)
|
||||
if err != nil || ifIndex < 0 {
|
||||
return nil, errors.New("interface name must be utunN, where N is a number, e.g. utun9, utun11 and so on")
|
||||
}
|
||||
|
||||
fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, sysprotoControl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctlInfo := &unix.CtlInfo{}
|
||||
copy(ctlInfo.Name[:], utunControlName)
|
||||
if err := unix.IoctlCtlInfo(fd, ctlInfo); err != nil {
|
||||
_ = unix.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sockaddr := &unix.SockaddrCtl{
|
||||
ID: ctlInfo.Id,
|
||||
Unit: uint32(ifIndex) + 1,
|
||||
}
|
||||
if err := unix.Connect(fd, sockaddr); err != nil {
|
||||
_ = unix.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := unix.SetNonblock(fd, true); err != nil {
|
||||
_ = unix.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return os.NewFile(uintptr(fd), name), nil
|
||||
}
|
||||
|
||||
// setup the interface by name
|
||||
func setup(name string, MTU uint32) error {
|
||||
if err := setMTU(name, MTU); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
* Darwin routing require tunnel type interface to have local and remote address, to be routable.
|
||||
* To simplify inevitable task, assign the interface static ip address, which in current implementation
|
||||
* is just some random ip from link-local pool, allowing to not bother about existing routing intersection.
|
||||
*/
|
||||
syntheticIP, _ := netip.ParsePrefix(gateway)
|
||||
if err := setIPAddress(name, syntheticIP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setMTU sets MTU on the interface by given name
|
||||
func setMTU(name string, mtu uint32) error {
|
||||
socket, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer unix.Close(socket)
|
||||
|
||||
ifr := unix.IfreqMTU{MTU: int32(mtu)}
|
||||
copy(ifr.Name[:], name)
|
||||
return unix.IoctlSetIfreqMTU(socket, &ifr)
|
||||
}
|
||||
|
||||
type ifAliasReq4 struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Addr unix.RawSockaddrInet4
|
||||
Dstaddr unix.RawSockaddrInet4
|
||||
Mask unix.RawSockaddrInet4
|
||||
}
|
||||
|
||||
type ifAliasReq6 struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Addr unix.RawSockaddrInet6
|
||||
Dstaddr unix.RawSockaddrInet6
|
||||
Mask unix.RawSockaddrInet6
|
||||
Flags uint32
|
||||
Lifetime addrLifetime6
|
||||
}
|
||||
|
||||
type addrLifetime6 struct {
|
||||
Expire float64
|
||||
Preferred float64
|
||||
Vltime uint32
|
||||
Pltime uint32
|
||||
}
|
||||
|
||||
// setIPAddress sets ipv4 and ipv6 addresses to the interface, required for the routing to work
|
||||
func setIPAddress(name string, gateway netip.Prefix) error {
|
||||
socket4, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer unix.Close(socket4)
|
||||
|
||||
// assume local ip address is next one from the remote address
|
||||
local4 := gateway.Addr().As4()
|
||||
local4[3]++
|
||||
|
||||
// fill the configuration for ipv4
|
||||
ifReq4 := ifAliasReq4{
|
||||
Addr: unix.RawSockaddrInet4{
|
||||
Len: unix.SizeofSockaddrInet4,
|
||||
Family: unix.AF_INET,
|
||||
Addr: local4,
|
||||
},
|
||||
Dstaddr: unix.RawSockaddrInet4{
|
||||
Len: unix.SizeofSockaddrInet4,
|
||||
Family: unix.AF_INET,
|
||||
Addr: gateway.Addr().As4(),
|
||||
},
|
||||
Mask: unix.RawSockaddrInet4{
|
||||
Len: unix.SizeofSockaddrInet4,
|
||||
Family: unix.AF_INET,
|
||||
Addr: netip.MustParseAddr(net.IP(net.CIDRMask(gateway.Bits(), 32)).String()).As4(),
|
||||
},
|
||||
}
|
||||
copy(ifReq4.Name[:], name)
|
||||
if err = ioctlPtr(socket4, unix.SIOCAIFADDR, unsafe.Pointer(&ifReq4)); err != nil {
|
||||
return os.NewSyscallError("SIOCAIFADDR", err)
|
||||
}
|
||||
|
||||
socket6, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer unix.Close(socket6)
|
||||
|
||||
// link-local ipv6 address with suffix from ipv6
|
||||
local6 := netip.AddrFrom16([16]byte{0: 0xfe, 1: 0x80, 12: local4[0], 13: local4[1], 14: local4[2], 15: local4[3]})
|
||||
|
||||
// fill the configuration for ipv6
|
||||
// only link-local address without the destination is enough for it
|
||||
ifReq6 := ifAliasReq6{
|
||||
Addr: unix.RawSockaddrInet6{
|
||||
Len: unix.SizeofSockaddrInet6,
|
||||
Family: unix.AF_INET6,
|
||||
Addr: local6.As16(),
|
||||
},
|
||||
Mask: unix.RawSockaddrInet6{
|
||||
Len: unix.SizeofSockaddrInet6,
|
||||
Family: unix.AF_INET6,
|
||||
Addr: netip.MustParseAddr(net.IP(net.CIDRMask(64, 128)).String()).As16(),
|
||||
},
|
||||
Flags: IN6_IFF_NODAD,
|
||||
Lifetime: addrLifetime6{
|
||||
Vltime: ND6_INFINITE_LIFETIME,
|
||||
Pltime: ND6_INFINITE_LIFETIME,
|
||||
},
|
||||
}
|
||||
// assign link-local ipv6 address to the interface.
|
||||
// this will additionally trigger OS level autoconfiguration, which will result two different link-local
|
||||
// addresses - the requested one, and autoconfigured one.
|
||||
// this really has no known side effects, just look excessive. and actually considered pretty normal way to
|
||||
// enable the ipv6 on the interface by macOS concepts.
|
||||
copy(ifReq6.Name[:], name)
|
||||
if err = ioctlPtr(socket6, SIOCAIFADDR6, unsafe.Pointer(&ifReq6)); err != nil {
|
||||
return os.NewSyscallError("SIOCAIFADDR6", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) error {
|
||||
_, _, errno := unix.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !linux && !windows && !android
|
||||
//go:build !linux && !windows && !android && !darwin
|
||||
|
||||
package tun
|
||||
|
||||
|
||||
+70
-10
@@ -3,19 +3,28 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
_ "unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wintun"
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
//go:linkname procyield runtime.procyield
|
||||
func procyield(cycles uint32)
|
||||
|
||||
// WindowsTun is an object that handles tun network interface on Windows
|
||||
// current version is heavily stripped to do nothing more,
|
||||
// then create a network interface, to be provided as endpoint to gVisor ip stack
|
||||
type WindowsTun struct {
|
||||
options TunOptions
|
||||
adapter *wintun.Adapter
|
||||
session wintun.Session
|
||||
MTU uint32
|
||||
options TunOptions
|
||||
adapter *wintun.Adapter
|
||||
session wintun.Session
|
||||
readWait windows.Handle
|
||||
MTU uint32
|
||||
}
|
||||
|
||||
// WindowsTun implements Tun
|
||||
@@ -24,6 +33,9 @@ var _ Tun = (*WindowsTun)(nil)
|
||||
// WindowsTun implements GVisorTun
|
||||
var _ GVisorTun = (*WindowsTun)(nil)
|
||||
|
||||
// WindowsTun implements GVisorDevice
|
||||
var _ GVisorDevice = (*WindowsTun)(nil)
|
||||
|
||||
// NewTun creates a Wintun interface with the given name. Should a Wintun
|
||||
// interface with the same name exist, it tried to be reused.
|
||||
func NewTun(options TunOptions) (Tun, error) {
|
||||
@@ -41,9 +53,10 @@ func NewTun(options TunOptions) (Tun, error) {
|
||||
}
|
||||
|
||||
tun := &WindowsTun{
|
||||
options: options,
|
||||
adapter: adapter,
|
||||
session: session,
|
||||
options: options,
|
||||
adapter: adapter,
|
||||
session: session,
|
||||
readWait: session.ReadWaitEvent(),
|
||||
// there is currently no iphndl.dll support, which is the netlink library for windows
|
||||
// so there is nowhere to change MTU for the Wintun interface, and we take its default value
|
||||
MTU: wintun.PacketSizeMax,
|
||||
@@ -78,7 +91,54 @@ func (t *WindowsTun) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// newEndpoint builds new gVisor stack.LinkEndpoint (WintunEndpoint) on top of WindowsTun
|
||||
func (t *WindowsTun) newEndpoint() (stack.LinkEndpoint, error) {
|
||||
return &WintunEndpoint{tun: t}, nil
|
||||
// WritePacket implements GVisorDevice method to write one packet to the tun device
|
||||
func (t *WindowsTun) WritePacket(packetBuffer *stack.PacketBuffer) tcpip.Error {
|
||||
// request buffer from Wintun
|
||||
packet, err := t.session.AllocateSendPacket(packetBuffer.Size())
|
||||
if err != nil {
|
||||
return &tcpip.ErrAborted{}
|
||||
}
|
||||
|
||||
// copy the bytes of slices that compose the packet into the allocated buffer
|
||||
var index int
|
||||
for _, packetElement := range packetBuffer.AsSlices() {
|
||||
index += copy(packet[index:], packetElement)
|
||||
}
|
||||
|
||||
// signal Wintun to send that buffer as the packet
|
||||
t.session.SendPacket(packet)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPacket implements GVisorDevice method to read one packet from the tun device
|
||||
// It is expected that the method will not block, rather return ErrQueueEmpty when there is nothing on the line,
|
||||
// which will make the stack call Wait which should implement desired push-back
|
||||
func (t *WindowsTun) ReadPacket() (byte, *stack.PacketBuffer, error) {
|
||||
packet, err := t.session.ReceivePacket()
|
||||
if errors.Is(err, windows.ERROR_NO_MORE_ITEMS) {
|
||||
return 0, nil, ErrQueueEmpty
|
||||
}
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
version := packet[0] >> 4
|
||||
packetBuffer := buffer.MakeWithView(buffer.NewViewWithData(packet))
|
||||
return version, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: packetBuffer,
|
||||
IsForwardedPacket: true,
|
||||
OnRelease: func() {
|
||||
t.session.ReleaseReceivePacket(packet)
|
||||
},
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (t *WindowsTun) Wait() {
|
||||
procyield(1)
|
||||
_, _ = windows.WaitForSingleObject(t.readWait, windows.INFINITE)
|
||||
}
|
||||
|
||||
func (t *WindowsTun) newEndpoint() (stack.LinkEndpoint, error) {
|
||||
return &LinkEndpoint{deviceMTU: t.options.MTU, device: t}, nil
|
||||
}
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
//go:build windows
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
_ "unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// WintunEndpoint implements GVisor stack.LinkEndpoint
|
||||
var _ stack.LinkEndpoint = (*WintunEndpoint)(nil)
|
||||
|
||||
type WintunEndpoint struct {
|
||||
tun *WindowsTun
|
||||
dispatcherCancel context.CancelFunc
|
||||
}
|
||||
|
||||
var ErrUnsupportedNetworkProtocol = errors.New("unsupported ip version")
|
||||
|
||||
//go:linkname procyield runtime.procyield
|
||||
func procyield(cycles uint32)
|
||||
|
||||
func (e *WintunEndpoint) MTU() uint32 {
|
||||
return e.tun.MTU
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) SetMTU(mtu uint32) {
|
||||
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) MaxHeaderLength() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) LinkAddress() tcpip.LinkAddress {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) SetLinkAddress(addr tcpip.LinkAddress) {
|
||||
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||
return stack.CapabilityRXChecksumOffload
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
if e.dispatcherCancel != nil {
|
||||
e.dispatcherCancel()
|
||||
e.dispatcherCancel = nil
|
||||
}
|
||||
|
||||
if dispatcher != nil {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go e.dispatchLoop(ctx, dispatcher)
|
||||
e.dispatcherCancel = cancel
|
||||
}
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) IsAttached() bool {
|
||||
return e.dispatcherCancel != nil
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) Wait() {
|
||||
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
return header.ARPHardwareNone
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) AddHeader(buffer *stack.PacketBuffer) {
|
||||
// tun interface doesn't have link layer header, it will be added by the OS
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) ParseHeader(ptr *stack.PacketBuffer) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) Close() {
|
||||
if e.dispatcherCancel != nil {
|
||||
e.dispatcherCancel()
|
||||
e.dispatcherCancel = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) SetOnCloseAction(f func()) {
|
||||
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
||||
var n int
|
||||
// for all packets in the list to send
|
||||
for _, packetBuffer := range packetBufferList.AsSlice() {
|
||||
// request buffer from Wintun
|
||||
packet, err := e.tun.session.AllocateSendPacket(packetBuffer.Size())
|
||||
if err != nil {
|
||||
return n, &tcpip.ErrAborted{}
|
||||
}
|
||||
|
||||
// copy the bytes of slices that compose the packet into the allocated buffer
|
||||
var index int
|
||||
for _, packetElement := range packetBuffer.AsSlices() {
|
||||
index += copy(packet[index:], packetElement)
|
||||
}
|
||||
|
||||
// signal Wintun to send that buffer as the packet
|
||||
e.tun.session.SendPacket(packet)
|
||||
n++
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) readPacket() (tcpip.NetworkProtocolNumber, *stack.PacketBuffer, error) {
|
||||
packet, err := e.tun.session.ReceivePacket()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
var networkProtocol tcpip.NetworkProtocolNumber
|
||||
switch header.IPVersion(packet) {
|
||||
case header.IPv4Version:
|
||||
networkProtocol = header.IPv4ProtocolNumber
|
||||
case header.IPv6Version:
|
||||
networkProtocol = header.IPv6ProtocolNumber
|
||||
default:
|
||||
e.tun.session.ReleaseReceivePacket(packet)
|
||||
return 0, nil, ErrUnsupportedNetworkProtocol
|
||||
}
|
||||
|
||||
packetBuffer := buffer.MakeWithView(buffer.NewViewWithData(packet))
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: packetBuffer,
|
||||
IsForwardedPacket: true,
|
||||
OnRelease: func() {
|
||||
e.tun.session.ReleaseReceivePacket(packet)
|
||||
},
|
||||
})
|
||||
return networkProtocol, pkt, nil
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) dispatchLoop(ctx context.Context, dispatcher stack.NetworkDispatcher) {
|
||||
readWait := e.tun.session.ReadWaitEvent()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
networkProtocolNumber, packet, err := e.readPacket()
|
||||
// read queue empty, yield slightly, wait for the spinlock, retry
|
||||
if errors.Is(err, windows.ERROR_NO_MORE_ITEMS) {
|
||||
procyield(1)
|
||||
_, _ = windows.WaitForSingleObject(readWait, windows.INFINITE)
|
||||
continue
|
||||
}
|
||||
// discard unknown network protocol packet
|
||||
if errors.Is(err, ErrUnsupportedNetworkProtocol) {
|
||||
continue
|
||||
}
|
||||
// stop dispatcher loop on any other interface failure
|
||||
if err != nil {
|
||||
e.Attach(nil)
|
||||
continue
|
||||
}
|
||||
|
||||
// dispatch the buffer to the stack
|
||||
dispatcher.DeliverNetworkPacket(networkProtocolNumber, packet)
|
||||
// signal the buffer that it can be released
|
||||
packet.DecRef()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
)
|
||||
|
||||
// sub-handler specifically for udp connections under main handler
|
||||
type udpConnectionHandler struct {
|
||||
sync.Mutex
|
||||
|
||||
udpConns map[net.Destination]*udpConn
|
||||
|
||||
handleConnection func(conn net.Conn, dest net.Destination)
|
||||
writePacket func(data []byte, src net.Destination, dst net.Destination) error
|
||||
}
|
||||
|
||||
func newUdpConnectionHandler(handleConnection func(conn net.Conn, dest net.Destination), writePacket func(data []byte, src net.Destination, dst net.Destination) error) *udpConnectionHandler {
|
||||
handler := &udpConnectionHandler{
|
||||
udpConns: make(map[net.Destination]*udpConn),
|
||||
handleConnection: handleConnection,
|
||||
writePacket: writePacket,
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
// HandlePacket handles UDP packets coming from tun, to forward to the dispatcher
|
||||
// this custom handler support FullCone NAT of returning packets, binding connection only by the source addr:port
|
||||
func (u *udpConnectionHandler) HandlePacket(src net.Destination, dst net.Destination, data []byte) bool {
|
||||
u.Lock()
|
||||
conn, found := u.udpConns[src]
|
||||
if !found {
|
||||
egress := make(chan []byte, 16)
|
||||
conn = &udpConn{handler: u, egress: egress, src: src, dst: dst}
|
||||
u.udpConns[src] = conn
|
||||
|
||||
go u.handleConnection(conn, dst)
|
||||
}
|
||||
u.Unlock()
|
||||
|
||||
// send packet data to the egress channel, if it has buffer, or discard
|
||||
select {
|
||||
case conn.egress <- data:
|
||||
default:
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (u *udpConnectionHandler) connectionFinished(src net.Destination) {
|
||||
u.Lock()
|
||||
conn, found := u.udpConns[src]
|
||||
if found {
|
||||
delete(u.udpConns, src)
|
||||
close(conn.egress)
|
||||
}
|
||||
u.Unlock()
|
||||
}
|
||||
|
||||
// udp connection abstraction
|
||||
type udpConn struct {
|
||||
net.Conn
|
||||
buf.Writer
|
||||
|
||||
handler *udpConnectionHandler
|
||||
|
||||
egress chan []byte
|
||||
src net.Destination
|
||||
dst net.Destination
|
||||
}
|
||||
|
||||
// Read packets from the connection
|
||||
func (c *udpConn) Read(p []byte) (int, error) {
|
||||
data, ok := <-c.egress
|
||||
if !ok {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n := copy(p, data)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Write returning packets back
|
||||
func (c *udpConn) Write(p []byte) (int, error) {
|
||||
// sending packets back mean sending payload with source/destination reversed
|
||||
err := c.handler.writePacket(p, c.dst, c.src)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *udpConn) Close() error {
|
||||
c.handler.connectionFinished(c.src)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpConn) LocalAddr() net.Addr {
|
||||
return &net.UDPAddr{IP: c.dst.Address.IP(), Port: int(c.dst.Port.Value())}
|
||||
}
|
||||
|
||||
func (c *udpConn) RemoteAddr() net.Addr {
|
||||
return &net.UDPAddr{IP: c.src.Address.IP(), Port: int(c.src.Port.Value())}
|
||||
}
|
||||
|
||||
// Write returning packets back
|
||||
func (c *udpConn) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
for _, b := range mb {
|
||||
dst := c.dst
|
||||
if b.UDP != nil {
|
||||
dst = *b.UDP
|
||||
}
|
||||
|
||||
// validate address family matches between buffer packet and the connection
|
||||
if dst.Address.Family() != c.dst.Address.Family() {
|
||||
continue
|
||||
}
|
||||
|
||||
// sending packets back mean sending payload with source/destination reversed
|
||||
err := c.handler.writePacket(b.Bytes(), dst, c.src)
|
||||
if err != nil {
|
||||
// udp doesn't guarantee delivery, so in any failure we just continue to the next packet
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -68,12 +68,12 @@ func (aidd *AuthIDDecoder) Decode(data [16]byte) (int64, uint32, int32, []byte)
|
||||
}
|
||||
|
||||
func NewAuthIDDecoderHolder() *AuthIDDecoderHolder {
|
||||
return &AuthIDDecoderHolder{make(map[string]*AuthIDDecoderItem), antireplay.NewReplayFilter(120)}
|
||||
return &AuthIDDecoderHolder{make(map[string]*AuthIDDecoderItem), antireplay.NewMapFilter[[16]byte](120)}
|
||||
}
|
||||
|
||||
type AuthIDDecoderHolder struct {
|
||||
decoders map[string]*AuthIDDecoderItem
|
||||
filter *antireplay.ReplayFilter
|
||||
filter *antireplay.ReplayFilter[[16]byte]
|
||||
}
|
||||
|
||||
type AuthIDDecoderItem struct {
|
||||
@@ -111,7 +111,7 @@ func (a *AuthIDDecoderHolder) Match(authID [16]byte) (interface{}, error) {
|
||||
return nil, ErrInvalidTime
|
||||
}
|
||||
|
||||
if !a.filter.Check(authID[:]) {
|
||||
if !a.filter.Check(authID) {
|
||||
return nil, ErrReplay
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousMo
|
||||
})
|
||||
stack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
||||
|
||||
udpForwarder := udp.NewForwarder(stack, func(r *udp.ForwarderRequest) {
|
||||
udpForwarder := udp.NewForwarder(stack, func(r *udp.ForwarderRequest) bool {
|
||||
go func(r *udp.ForwarderRequest) {
|
||||
var (
|
||||
wq waiter.Queue
|
||||
@@ -195,6 +195,8 @@ func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousMo
|
||||
|
||||
handler(net.UDPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort)), gonet.NewUDPConn(&wq, ep))
|
||||
}(r)
|
||||
|
||||
return true
|
||||
})
|
||||
stack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
||||
}
|
||||
|
||||
+30
-17
@@ -213,27 +213,40 @@ func testTCPConn2(conn net.Conn, payloadSize int, timeout time.Duration) func()
|
||||
"\tSys =", units.ByteSize(m.Sys).String(),
|
||||
"\tNumGC =", m.NumGC)
|
||||
}()
|
||||
payload := make([]byte, payloadSize)
|
||||
common.Must2(rand.Read(payload))
|
||||
singleWrite := func(length int) error {
|
||||
payload := make([]byte, length)
|
||||
common.Must2(rand.Read(payload))
|
||||
|
||||
nBytes, err := conn.Write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if nBytes != len(payload) {
|
||||
return errors.New("expect ", len(payload), " written, but actually ", nBytes)
|
||||
}
|
||||
nBytes, err := conn.Write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if nBytes != len(payload) {
|
||||
return errors.New("expect ", len(payload), " written, but actually ", nBytes)
|
||||
}
|
||||
|
||||
response, err := readFrom2(conn, timeout, payloadSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = response
|
||||
response, err := readFrom2(conn, timeout, length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = response
|
||||
|
||||
if r := bytes.Compare(response, xor(payload)); r != 0 {
|
||||
return errors.New(r)
|
||||
}
|
||||
if r := bytes.Compare(response, xor(payload)); r != 0 {
|
||||
return errors.New(r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
for payloadSize > 0 {
|
||||
sizeToWrite := 1024
|
||||
if payloadSize < 1024 {
|
||||
sizeToWrite = payloadSize
|
||||
}
|
||||
if err := singleWrite(sizeToWrite); err != nil {
|
||||
return err
|
||||
}
|
||||
payloadSize -= sizeToWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ func TestSimpleTLSConnection(t *testing.T) {
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -48,7 +50,7 @@ func TestSimpleTLSConnection(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(ct)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -92,7 +94,7 @@ func TestSimpleTLSConnection(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -104,7 +106,7 @@ func TestSimpleTLSConnection(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{ctHash[:]},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -203,7 +205,7 @@ func TestAutoIssuingCertificate(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -247,6 +249,8 @@ func TestTLSOverKCP(t *testing.T) {
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := udp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -260,7 +264,7 @@ func TestTLSOverKCP(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(ct)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -304,7 +308,7 @@ func TestTLSOverKCP(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -317,7 +321,7 @@ func TestTLSOverKCP(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{ctHash[:]},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -343,6 +347,8 @@ func TestTLSOverWebSocket(t *testing.T) {
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -356,7 +362,7 @@ func TestTLSOverWebSocket(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(ct)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -400,7 +406,7 @@ func TestTLSOverWebSocket(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -419,7 +425,7 @@ func TestTLSOverWebSocket(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{ctHash[:]},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -449,6 +455,8 @@ func TestGRPC(t *testing.T) {
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -468,7 +476,7 @@ func TestGRPC(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(ct)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -512,7 +520,7 @@ func TestGRPC(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -531,7 +539,7 @@ func TestGRPC(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{ctHash[:]},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -561,6 +569,8 @@ func TestGRPCMultiMode(t *testing.T) {
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -580,7 +590,7 @@ func TestGRPCMultiMode(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(ct)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -624,7 +634,7 @@ func TestGRPCMultiMode(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -643,7 +653,7 @@ func TestGRPCMultiMode(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{ctHash[:]},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -672,9 +682,9 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||
dest, err := tcpServer.Start()
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
certificateDer := cert.MustGenerate(nil)
|
||||
certificateDer, _ := cert.MustGenerate(nil)
|
||||
certificate := tls.ParseCertificate(certificateDer)
|
||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
||||
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -731,7 +741,7 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -743,8 +753,7 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -769,9 +778,9 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
dest, err := tcpServer.Start()
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
certificateDer := cert.MustGenerate(nil)
|
||||
certificateDer, _ := cert.MustGenerate(nil)
|
||||
certificate := tls.ParseCertificate(certificateDer)
|
||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
||||
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||
certHash[1] += 1
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
@@ -829,7 +838,7 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -841,8 +850,7 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -867,9 +875,9 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||
dest, err := tcpServer.Start()
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
certificateDer := cert.MustGenerate(nil)
|
||||
certificateDer, _ := cert.MustGenerate(nil)
|
||||
certificate := tls.ParseCertificate(certificateDer)
|
||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
||||
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -926,7 +934,7 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -938,9 +946,8 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Fingerprint: "random",
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
||||
Fingerprint: "random",
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -965,9 +972,9 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
dest, err := tcpServer.Start()
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
certificateDer := cert.MustGenerate(nil)
|
||||
certificateDer, _ := cert.MustGenerate(nil)
|
||||
certificate := tls.ParseCertificate(certificateDer)
|
||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
||||
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||
certHash[1] += 1
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
@@ -1025,7 +1032,7 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
Receiver: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -1037,9 +1044,8 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Fingerprint: "random",
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
||||
Fingerprint: "random",
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -97,7 +97,7 @@ func TestVless(t *testing.T) {
|
||||
Vnext: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vless.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -129,6 +129,8 @@ func TestVlessTls(t *testing.T) {
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -148,7 +150,7 @@ func TestVlessTls(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(ct)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -198,7 +200,7 @@ func TestVlessTls(t *testing.T) {
|
||||
Vnext: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vless.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
@@ -217,7 +219,7 @@ func TestVlessTls(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{ctHash[:]},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -247,6 +249,8 @@ func TestVlessXtlsVision(t *testing.T) {
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
userID := protocol.NewID(uuid.New())
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
@@ -266,7 +270,7 @@ func TestVlessXtlsVision(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(ct)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -317,7 +321,7 @@ func TestVlessXtlsVision(t *testing.T) {
|
||||
Vnext: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vless.Account{
|
||||
Id: userID.String(),
|
||||
Flow: vless.XRV,
|
||||
@@ -337,7 +341,7 @@ func TestVlessXtlsVision(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{ctHash[:]},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -447,7 +451,7 @@ func TestVlessXtlsVisionReality(t *testing.T) {
|
||||
Vnext: &protocol.ServerEndpoint{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: &protocol.User{
|
||||
User: &protocol.User{
|
||||
Account: serial.ToTypedMessage(&vless.Account{
|
||||
Id: userID.String(),
|
||||
Flow: vless.XRV,
|
||||
|
||||
@@ -90,7 +90,7 @@ func (c *StreamConfig) GetEffectiveSecuritySettings() (interface{}, error) {
|
||||
}
|
||||
|
||||
func (c *StreamConfig) HasSecuritySettings() bool {
|
||||
return len(c.SecurityType) > 0
|
||||
return len(c.SecuritySettings) > 0
|
||||
}
|
||||
|
||||
func (c *ProxyConfig) HasTag() bool {
|
||||
@@ -130,7 +130,7 @@ func (s DomainStrategy) FallbackIP6() bool {
|
||||
}
|
||||
|
||||
func (s DomainStrategy) GetDynamicStrategy(addrFamily net.AddressFamily) DomainStrategy {
|
||||
if addrFamily.IsDomain(){
|
||||
if addrFamily.IsDomain() {
|
||||
return s
|
||||
}
|
||||
switch s {
|
||||
|
||||
+158
-224
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.35.1
|
||||
// protoc v5.28.2
|
||||
// protoc-gen-go v1.36.10
|
||||
// protoc v6.33.1
|
||||
// source: transport/internet/config.proto
|
||||
|
||||
package internet
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -209,14 +210,13 @@ func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) {
|
||||
}
|
||||
|
||||
type TransportConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Transport protocol name.
|
||||
ProtocolName string `protobuf:"bytes,3,opt,name=protocol_name,json=protocolName,proto3" json:"protocol_name,omitempty"`
|
||||
// Specific transport protocol settings.
|
||||
Settings *serial.TypedMessage `protobuf:"bytes,2,opt,name=settings,proto3" json:"settings,omitempty"`
|
||||
Settings *serial.TypedMessage `protobuf:"bytes,2,opt,name=settings,proto3" json:"settings,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TransportConfig) Reset() {
|
||||
@@ -264,12 +264,9 @@ func (x *TransportConfig) GetSettings() *serial.TypedMessage {
|
||||
}
|
||||
|
||||
type StreamConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Address *net.IPOrDomain `protobuf:"bytes,8,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Port uint32 `protobuf:"varint,9,opt,name=port,proto3" json:"port,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Address *net.IPOrDomain `protobuf:"bytes,8,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Port uint32 `protobuf:"varint,9,opt,name=port,proto3" json:"port,omitempty"`
|
||||
// Effective network.
|
||||
ProtocolName string `protobuf:"bytes,5,opt,name=protocol_name,json=protocolName,proto3" json:"protocol_name,omitempty"`
|
||||
TransportSettings []*TransportConfig `protobuf:"bytes,2,rep,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"`
|
||||
@@ -277,7 +274,11 @@ type StreamConfig struct {
|
||||
SecurityType string `protobuf:"bytes,3,opt,name=security_type,json=securityType,proto3" json:"security_type,omitempty"`
|
||||
// Transport security settings. They can be either TLS or REALITY.
|
||||
SecuritySettings []*serial.TypedMessage `protobuf:"bytes,4,rep,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"`
|
||||
Udpmasks []*serial.TypedMessage `protobuf:"bytes,10,rep,name=udpmasks,proto3" json:"udpmasks,omitempty"`
|
||||
Tcpmasks []*serial.TypedMessage `protobuf:"bytes,11,rep,name=tcpmasks,proto3" json:"tcpmasks,omitempty"`
|
||||
SocketSettings *SocketConfig `protobuf:"bytes,6,opt,name=socket_settings,json=socketSettings,proto3" json:"socket_settings,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *StreamConfig) Reset() {
|
||||
@@ -352,6 +353,20 @@ func (x *StreamConfig) GetSecuritySettings() []*serial.TypedMessage {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StreamConfig) GetUdpmasks() []*serial.TypedMessage {
|
||||
if x != nil {
|
||||
return x.Udpmasks
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StreamConfig) GetTcpmasks() []*serial.TypedMessage {
|
||||
if x != nil {
|
||||
return x.Tcpmasks
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StreamConfig) GetSocketSettings() *SocketConfig {
|
||||
if x != nil {
|
||||
return x.SocketSettings
|
||||
@@ -360,12 +375,11 @@ func (x *StreamConfig) GetSocketSettings() *SocketConfig {
|
||||
}
|
||||
|
||||
type ProxyConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
TransportLayerProxy bool `protobuf:"varint,2,opt,name=transportLayerProxy,proto3" json:"transportLayerProxy,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
TransportLayerProxy bool `protobuf:"varint,2,opt,name=transportLayerProxy,proto3" json:"transportLayerProxy,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ProxyConfig) Reset() {
|
||||
@@ -413,16 +427,15 @@ func (x *ProxyConfig) GetTransportLayerProxy() bool {
|
||||
}
|
||||
|
||||
type CustomSockopt struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
System string `protobuf:"bytes,1,opt,name=system,proto3" json:"system,omitempty"`
|
||||
Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"`
|
||||
Level string `protobuf:"bytes,3,opt,name=level,proto3" json:"level,omitempty"`
|
||||
Opt string `protobuf:"bytes,4,opt,name=opt,proto3" json:"opt,omitempty"`
|
||||
Value string `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
|
||||
Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
System string `protobuf:"bytes,1,opt,name=system,proto3" json:"system,omitempty"`
|
||||
Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"`
|
||||
Level string `protobuf:"bytes,3,opt,name=level,proto3" json:"level,omitempty"`
|
||||
Opt string `protobuf:"bytes,4,opt,name=opt,proto3" json:"opt,omitempty"`
|
||||
Value string `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
|
||||
Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"`
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CustomSockopt) Reset() {
|
||||
@@ -499,10 +512,7 @@ func (x *CustomSockopt) GetType() string {
|
||||
|
||||
// SocketConfig is options to be applied on network sockets.
|
||||
type SocketConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Mark of the connection. If non-zero, the value will be set to SO_MARK.
|
||||
Mark int32 `protobuf:"varint,1,opt,name=mark,proto3" json:"mark,omitempty"`
|
||||
// TFO is the state of TFO settings.
|
||||
@@ -531,6 +541,8 @@ type SocketConfig struct {
|
||||
AddressPortStrategy AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"`
|
||||
HappyEyeballs *HappyEyeballsConfig `protobuf:"bytes,22,opt,name=happy_eyeballs,json=happyEyeballs,proto3" json:"happy_eyeballs,omitempty"`
|
||||
TrustedXForwardedFor []string `protobuf:"bytes,23,rep,name=trusted_x_forwarded_for,json=trustedXForwardedFor,proto3" json:"trusted_x_forwarded_for,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SocketConfig) Reset() {
|
||||
@@ -725,14 +737,13 @@ func (x *SocketConfig) GetTrustedXForwardedFor() []string {
|
||||
}
|
||||
|
||||
type HappyEyeballsConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PrioritizeIpv6 bool `protobuf:"varint,1,opt,name=prioritize_ipv6,json=prioritizeIpv6,proto3" json:"prioritize_ipv6,omitempty"`
|
||||
Interleave uint32 `protobuf:"varint,2,opt,name=interleave,proto3" json:"interleave,omitempty"`
|
||||
TryDelayMs uint64 `protobuf:"varint,3,opt,name=try_delayMs,json=tryDelayMs,proto3" json:"try_delayMs,omitempty"`
|
||||
MaxConcurrentTry uint32 `protobuf:"varint,4,opt,name=max_concurrent_try,json=maxConcurrentTry,proto3" json:"max_concurrent_try,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
PrioritizeIpv6 bool `protobuf:"varint,1,opt,name=prioritize_ipv6,json=prioritizeIpv6,proto3" json:"prioritize_ipv6,omitempty"`
|
||||
Interleave uint32 `protobuf:"varint,2,opt,name=interleave,proto3" json:"interleave,omitempty"`
|
||||
TryDelayMs uint64 `protobuf:"varint,3,opt,name=try_delayMs,json=tryDelayMs,proto3" json:"try_delayMs,omitempty"`
|
||||
MaxConcurrentTry uint32 `protobuf:"varint,4,opt,name=max_concurrent_try,json=maxConcurrentTry,proto3" json:"max_concurrent_try,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *HappyEyeballsConfig) Reset() {
|
||||
@@ -795,184 +806,106 @@ func (x *HappyEyeballsConfig) GetMaxConcurrentTry() uint32 {
|
||||
|
||||
var File_transport_internet_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_config_proto_rawDesc = []byte{
|
||||
0x0a, 0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x12, 0x17, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x74, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x3c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||
0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x9b, 0x03,
|
||||
0x0a, 0x0c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35,
|
||||
0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65,
|
||||
0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x09, 0x20,
|
||||
0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x57,
|
||||
0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74,
|
||||
0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x53,
|
||||
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72,
|
||||
0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
|
||||
0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4d, 0x0a, 0x11,
|
||||
0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||
0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70,
|
||||
0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72,
|
||||
0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4e, 0x0a, 0x0f, 0x73,
|
||||
0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53,
|
||||
0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x6f, 0x63,
|
||||
0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x51, 0x0a, 0x0b, 0x50,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
|
||||
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x30, 0x0a, 0x13,
|
||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72,
|
||||
0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x22, 0x93,
|
||||
0x01, 0x0a, 0x0d, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74,
|
||||
0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77,
|
||||
0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6f, 0x70, 0x74, 0x18,
|
||||
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||
0x74, 0x79, 0x70, 0x65, 0x22, 0x89, 0x09, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74,
|
||||
0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x2e, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74,
|
||||
0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x41, 0x0a, 0x1d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
|
||||
0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x61,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65,
|
||||
0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73,
|
||||
0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x64,
|
||||
0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b,
|
||||
0x62, 0x69, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62,
|
||||
0x69, 0x6e, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08,
|
||||
0x62, 0x69, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65,
|
||||
0x70, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
|
||||
0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x50, 0x0a, 0x0f,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18,
|
||||
0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x21,
|
||||
0x0a, 0x0c, 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x09,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x12, 0x35, 0x0a, 0x17, 0x74, 0x63, 0x70, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c,
|
||||
0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01,
|
||||
0x28, 0x05, 0x52, 0x14, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65,
|
||||
0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x13, 0x74, 0x63, 0x70, 0x5f,
|
||||
0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x18,
|
||||
0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x74, 0x63, 0x70, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c,
|
||||
0x69, 0x76, 0x65, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63,
|
||||
0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0d, 0x74, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c,
|
||||
0x0a, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06,
|
||||
0x76, 0x36, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x36,
|
||||
0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x63, 0x70, 0x5f, 0x77, 0x69, 0x6e, 0x64,
|
||||
0x6f, 0x77, 0x5f, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e,
|
||||
0x74, 0x63, 0x70, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x43, 0x6c, 0x61, 0x6d, 0x70, 0x12, 0x28,
|
||||
0x0a, 0x10, 0x74, 0x63, 0x70, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f,
|
||||
0x75, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x63, 0x70, 0x55, 0x73, 0x65,
|
||||
0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x74, 0x63, 0x70, 0x5f,
|
||||
0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74,
|
||||
0x63, 0x70, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x6e, 0x65,
|
||||
0x74, 0x72, 0x61, 0x74, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x65, 0x6e,
|
||||
0x65, 0x74, 0x72, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, 0x6d, 0x70,
|
||||
0x74, 0x63, 0x70, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x63, 0x70, 0x4d, 0x70,
|
||||
0x74, 0x63, 0x70, 0x12, 0x4c, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63,
|
||||
0x6b, 0x6f, 0x70, 0x74, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f,
|
||||
0x70, 0x74, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70,
|
||||
0x74, 0x12, 0x60, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e,
|
||||
0x32, 0x2c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x13,
|
||||
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74,
|
||||
0x65, 0x67, 0x79, 0x12, 0x53, 0x0a, 0x0e, 0x68, 0x61, 0x70, 0x70, 0x79, 0x5f, 0x65, 0x79, 0x65,
|
||||
0x62, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x61, 0x70, 0x70, 0x79, 0x45, 0x79, 0x65, 0x62, 0x61,
|
||||
0x6c, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x68, 0x61, 0x70, 0x70, 0x79,
|
||||
0x45, 0x79, 0x65, 0x62, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x74, 0x72, 0x75, 0x73,
|
||||
0x74, 0x65, 0x64, 0x5f, 0x78, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x64, 0x5f,
|
||||
0x66, 0x6f, 0x72, 0x18, 0x17, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x74, 0x72, 0x75, 0x73, 0x74,
|
||||
0x65, 0x64, 0x58, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x22,
|
||||
0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a,
|
||||
0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79,
|
||||
0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02,
|
||||
0x22, 0xad, 0x01, 0x0a, 0x13, 0x48, 0x61, 0x70, 0x70, 0x79, 0x45, 0x79, 0x65, 0x62, 0x61, 0x6c,
|
||||
0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x6f,
|
||||
0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x5f, 0x69, 0x70, 0x76, 0x36, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x0e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x49, 0x70, 0x76,
|
||||
0x36, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x65, 0x61, 0x76,
|
||||
0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x73,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79,
|
||||
0x4d, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72,
|
||||
0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10,
|
||||
0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x79,
|
||||
0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74,
|
||||
0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a,
|
||||
0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53,
|
||||
0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49,
|
||||
0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36,
|
||||
0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05,
|
||||
0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d,
|
||||
0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a,
|
||||
0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a,
|
||||
0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a,
|
||||
0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x2a, 0x97, 0x01, 0x0a,
|
||||
0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61,
|
||||
0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0f,
|
||||
0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01, 0x12,
|
||||
0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c,
|
||||
0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e,
|
||||
0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x78,
|
||||
0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x54,
|
||||
0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x05, 0x12,
|
||||
0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
const file_transport_internet_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x1ftransport/internet/config.proto\x12\x17xray.transport.internet\x1a!common/serial/typed_message.proto\x1a\x18common/net/address.proto\"t\n" +
|
||||
"\x0fTransportConfig\x12#\n" +
|
||||
"\rprotocol_name\x18\x03 \x01(\tR\fprotocolName\x12<\n" +
|
||||
"\bsettings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\bsettings\"\x97\x04\n" +
|
||||
"\fStreamConfig\x125\n" +
|
||||
"\aaddress\x18\b \x01(\v2\x1b.xray.common.net.IPOrDomainR\aaddress\x12\x12\n" +
|
||||
"\x04port\x18\t \x01(\rR\x04port\x12#\n" +
|
||||
"\rprotocol_name\x18\x05 \x01(\tR\fprotocolName\x12W\n" +
|
||||
"\x12transport_settings\x18\x02 \x03(\v2(.xray.transport.internet.TransportConfigR\x11transportSettings\x12#\n" +
|
||||
"\rsecurity_type\x18\x03 \x01(\tR\fsecurityType\x12M\n" +
|
||||
"\x11security_settings\x18\x04 \x03(\v2 .xray.common.serial.TypedMessageR\x10securitySettings\x12<\n" +
|
||||
"\budpmasks\x18\n" +
|
||||
" \x03(\v2 .xray.common.serial.TypedMessageR\budpmasks\x12<\n" +
|
||||
"\btcpmasks\x18\v \x03(\v2 .xray.common.serial.TypedMessageR\btcpmasks\x12N\n" +
|
||||
"\x0fsocket_settings\x18\x06 \x01(\v2%.xray.transport.internet.SocketConfigR\x0esocketSettings\"Q\n" +
|
||||
"\vProxyConfig\x12\x10\n" +
|
||||
"\x03tag\x18\x01 \x01(\tR\x03tag\x120\n" +
|
||||
"\x13transportLayerProxy\x18\x02 \x01(\bR\x13transportLayerProxy\"\x93\x01\n" +
|
||||
"\rCustomSockopt\x12\x16\n" +
|
||||
"\x06system\x18\x01 \x01(\tR\x06system\x12\x18\n" +
|
||||
"\anetwork\x18\x02 \x01(\tR\anetwork\x12\x14\n" +
|
||||
"\x05level\x18\x03 \x01(\tR\x05level\x12\x10\n" +
|
||||
"\x03opt\x18\x04 \x01(\tR\x03opt\x12\x14\n" +
|
||||
"\x05value\x18\x05 \x01(\tR\x05value\x12\x12\n" +
|
||||
"\x04type\x18\x06 \x01(\tR\x04type\"\x89\t\n" +
|
||||
"\fSocketConfig\x12\x12\n" +
|
||||
"\x04mark\x18\x01 \x01(\x05R\x04mark\x12\x10\n" +
|
||||
"\x03tfo\x18\x02 \x01(\x05R\x03tfo\x12H\n" +
|
||||
"\x06tproxy\x18\x03 \x01(\x0e20.xray.transport.internet.SocketConfig.TProxyModeR\x06tproxy\x12A\n" +
|
||||
"\x1dreceive_original_dest_address\x18\x04 \x01(\bR\x1areceiveOriginalDestAddress\x12!\n" +
|
||||
"\fbind_address\x18\x05 \x01(\fR\vbindAddress\x12\x1b\n" +
|
||||
"\tbind_port\x18\x06 \x01(\rR\bbindPort\x122\n" +
|
||||
"\x15accept_proxy_protocol\x18\a \x01(\bR\x13acceptProxyProtocol\x12P\n" +
|
||||
"\x0fdomain_strategy\x18\b \x01(\x0e2'.xray.transport.internet.DomainStrategyR\x0edomainStrategy\x12!\n" +
|
||||
"\fdialer_proxy\x18\t \x01(\tR\vdialerProxy\x125\n" +
|
||||
"\x17tcp_keep_alive_interval\x18\n" +
|
||||
" \x01(\x05R\x14tcpKeepAliveInterval\x12-\n" +
|
||||
"\x13tcp_keep_alive_idle\x18\v \x01(\x05R\x10tcpKeepAliveIdle\x12%\n" +
|
||||
"\x0etcp_congestion\x18\f \x01(\tR\rtcpCongestion\x12\x1c\n" +
|
||||
"\tinterface\x18\r \x01(\tR\tinterface\x12\x16\n" +
|
||||
"\x06v6only\x18\x0e \x01(\bR\x06v6only\x12(\n" +
|
||||
"\x10tcp_window_clamp\x18\x0f \x01(\x05R\x0etcpWindowClamp\x12(\n" +
|
||||
"\x10tcp_user_timeout\x18\x10 \x01(\x05R\x0etcpUserTimeout\x12\x1e\n" +
|
||||
"\vtcp_max_seg\x18\x11 \x01(\x05R\ttcpMaxSeg\x12\x1c\n" +
|
||||
"\tpenetrate\x18\x12 \x01(\bR\tpenetrate\x12\x1b\n" +
|
||||
"\ttcp_mptcp\x18\x13 \x01(\bR\btcpMptcp\x12L\n" +
|
||||
"\rcustomSockopt\x18\x14 \x03(\v2&.xray.transport.internet.CustomSockoptR\rcustomSockopt\x12`\n" +
|
||||
"\x15address_port_strategy\x18\x15 \x01(\x0e2,.xray.transport.internet.AddressPortStrategyR\x13addressPortStrategy\x12S\n" +
|
||||
"\x0ehappy_eyeballs\x18\x16 \x01(\v2,.xray.transport.internet.HappyEyeballsConfigR\rhappyEyeballs\x125\n" +
|
||||
"\x17trusted_x_forwarded_for\x18\x17 \x03(\tR\x14trustedXForwardedFor\"/\n" +
|
||||
"\n" +
|
||||
"TProxyMode\x12\a\n" +
|
||||
"\x03Off\x10\x00\x12\n" +
|
||||
"\n" +
|
||||
"\x06TProxy\x10\x01\x12\f\n" +
|
||||
"\bRedirect\x10\x02\"\xad\x01\n" +
|
||||
"\x13HappyEyeballsConfig\x12'\n" +
|
||||
"\x0fprioritize_ipv6\x18\x01 \x01(\bR\x0eprioritizeIpv6\x12\x1e\n" +
|
||||
"\n" +
|
||||
"interleave\x18\x02 \x01(\rR\n" +
|
||||
"interleave\x12\x1f\n" +
|
||||
"\vtry_delayMs\x18\x03 \x01(\x04R\n" +
|
||||
"tryDelayMs\x12,\n" +
|
||||
"\x12max_concurrent_try\x18\x04 \x01(\rR\x10maxConcurrentTry*\xa9\x01\n" +
|
||||
"\x0eDomainStrategy\x12\t\n" +
|
||||
"\x05AS_IS\x10\x00\x12\n" +
|
||||
"\n" +
|
||||
"\x06USE_IP\x10\x01\x12\v\n" +
|
||||
"\aUSE_IP4\x10\x02\x12\v\n" +
|
||||
"\aUSE_IP6\x10\x03\x12\f\n" +
|
||||
"\bUSE_IP46\x10\x04\x12\f\n" +
|
||||
"\bUSE_IP64\x10\x05\x12\f\n" +
|
||||
"\bFORCE_IP\x10\x06\x12\r\n" +
|
||||
"\tFORCE_IP4\x10\a\x12\r\n" +
|
||||
"\tFORCE_IP6\x10\b\x12\x0e\n" +
|
||||
"\n" +
|
||||
"FORCE_IP46\x10\t\x12\x0e\n" +
|
||||
"\n" +
|
||||
"FORCE_IP64\x10\n" +
|
||||
"*\x97\x01\n" +
|
||||
"\x13AddressPortStrategy\x12\b\n" +
|
||||
"\x04None\x10\x00\x12\x0f\n" +
|
||||
"\vSrvPortOnly\x10\x01\x12\x12\n" +
|
||||
"\x0eSrvAddressOnly\x10\x02\x12\x15\n" +
|
||||
"\x11SrvPortAndAddress\x10\x03\x12\x0f\n" +
|
||||
"\vTxtPortOnly\x10\x04\x12\x12\n" +
|
||||
"\x0eTxtAddressOnly\x10\x05\x12\x15\n" +
|
||||
"\x11TxtPortAndAddress\x10\x06Bg\n" +
|
||||
"\x1bcom.xray.transport.internetP\x01Z,github.com/xtls/xray-core/transport/internet\xaa\x02\x17Xray.Transport.Internetb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_config_proto_rawDescData = file_transport_internet_config_proto_rawDesc
|
||||
file_transport_internet_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_config_proto_rawDescData)
|
||||
file_transport_internet_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_config_proto_rawDesc), len(file_transport_internet_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_config_proto_rawDescData
|
||||
}
|
||||
@@ -997,17 +930,19 @@ var file_transport_internet_config_proto_depIdxs = []int32{
|
||||
10, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||
3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
||||
9, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||
7, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||
2, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||
0, // 6: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||
6, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||
1, // 8: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||
8, // 9: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig
|
||||
10, // [10:10] is the sub-list for method output_type
|
||||
10, // [10:10] is the sub-list for method input_type
|
||||
10, // [10:10] is the sub-list for extension type_name
|
||||
10, // [10:10] is the sub-list for extension extendee
|
||||
0, // [0:10] is the sub-list for field type_name
|
||||
9, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
9, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
7, // 6: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||
2, // 7: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||
0, // 8: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||
6, // 9: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||
1, // 10: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||
8, // 11: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig
|
||||
12, // [12:12] is the sub-list for method output_type
|
||||
12, // [12:12] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_config_proto_init() }
|
||||
@@ -1019,7 +954,7 @@ func file_transport_internet_config_proto_init() {
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_transport_internet_config_proto_rawDesc,
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_config_proto_rawDesc), len(file_transport_internet_config_proto_rawDesc)),
|
||||
NumEnums: 3,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
@@ -1031,7 +966,6 @@ func file_transport_internet_config_proto_init() {
|
||||
MessageInfos: file_transport_internet_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_config_proto = out.File
|
||||
file_transport_internet_config_proto_rawDesc = nil
|
||||
file_transport_internet_config_proto_goTypes = nil
|
||||
file_transport_internet_config_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ message StreamConfig {
|
||||
// Transport security settings. They can be either TLS or REALITY.
|
||||
repeated xray.common.serial.TypedMessage security_settings = 4;
|
||||
|
||||
repeated xray.common.serial.TypedMessage udpmasks = 10;
|
||||
repeated xray.common.serial.TypedMessage tcpmasks = 11;
|
||||
|
||||
SocketConfig socket_settings = 6;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package finalmask
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
type Udpmask interface {
|
||||
UDP()
|
||||
|
||||
WrapConnClient(net.Conn) (net.Conn, error)
|
||||
WrapConnServer(net.Conn) (net.Conn, error)
|
||||
|
||||
WrapPacketConnClient(net.PacketConn) (net.PacketConn, error)
|
||||
WrapPacketConnServer(net.PacketConn) (net.PacketConn, error)
|
||||
|
||||
Size() int
|
||||
Serialize([]byte)
|
||||
}
|
||||
|
||||
type UdpmaskManager struct {
|
||||
udpmasks []Udpmask
|
||||
}
|
||||
|
||||
func NewUdpmaskManager(udpmasks []Udpmask) *UdpmaskManager {
|
||||
return &UdpmaskManager{
|
||||
udpmasks: udpmasks,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
var err error
|
||||
for _, mask := range m.udpmasks {
|
||||
raw, err = mask.WrapConnClient(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
var err error
|
||||
for _, mask := range m.udpmasks {
|
||||
raw, err = mask.WrapConnServer(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) {
|
||||
var err error
|
||||
for _, mask := range m.udpmasks {
|
||||
raw, err = mask.WrapPacketConnClient(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) {
|
||||
var err error
|
||||
for _, mask := range m.udpmasks {
|
||||
raw, err = mask.WrapPacketConnServer(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) Size() int {
|
||||
size := 0
|
||||
for _, mask := range m.udpmasks {
|
||||
size += mask.Size()
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) Serialize(b []byte) {
|
||||
index := 0
|
||||
for _, mask := range m.udpmasks {
|
||||
mask.Serialize(b[index:])
|
||||
index += mask.Size()
|
||||
}
|
||||
}
|
||||
|
||||
type Tcpmask interface {
|
||||
TCP()
|
||||
|
||||
WrapConnClient(net.Conn) (net.Conn, error)
|
||||
WrapConnServer(net.Conn) (net.Conn, error)
|
||||
}
|
||||
|
||||
type TcpmaskManager struct {
|
||||
tcpmasks []Tcpmask
|
||||
}
|
||||
|
||||
func NewTcpmaskManager(tcpmasks []Tcpmask) *TcpmaskManager {
|
||||
return &TcpmaskManager{
|
||||
tcpmasks: tcpmasks,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TcpmaskManager) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
var err error
|
||||
for _, mask := range m.tcpmasks {
|
||||
raw, err = mask.WrapConnClient(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (m *TcpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
var err error
|
||||
for _, mask := range m.tcpmasks {
|
||||
raw, err = mask.WrapConnServer(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package salamander
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/salamander/obfs"
|
||||
)
|
||||
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) {
|
||||
ob, err := obfs.NewSalamanderObfuscator([]byte(c.Password))
|
||||
if err != nil {
|
||||
return nil, errors.New("salamander err").Base(err)
|
||||
}
|
||||
return obfs.WrapPacketConn(raw, ob), nil
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) {
|
||||
ob, err := obfs.NewSalamanderObfuscator([]byte(c.Password))
|
||||
if err != nil {
|
||||
return nil, errors.New("salamander err").Base(err)
|
||||
}
|
||||
return obfs.WrapPacketConn(raw, ob), nil
|
||||
}
|
||||
|
||||
func (c *Config) Size() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *Config) Serialize([]byte) {
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.10
|
||||
// protoc v6.33.1
|
||||
// source: transport/internet/udpmask/salamander/config.proto
|
||||
|
||||
package salamander
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_udpmask_salamander_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_udpmask_salamander_config_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_udpmask_salamander_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_transport_internet_udpmask_salamander_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_udpmask_salamander_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"2transport/internet/udpmask/salamander/config.proto\x12*xray.transport.internet.udpmask.salamander\"$\n" +
|
||||
"\x06Config\x12\x1a\n" +
|
||||
"\bpassword\x18\x01 \x01(\tR\bpasswordB\xa0\x01\n" +
|
||||
".com.xray.transport.internet.udpmask.salamanderP\x01Z?github.com/xtls/xray-core/transport/internet/udpmask/salamander\xaa\x02*Xray.Transport.Internet.Udpmask.Salamanderb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_udpmask_salamander_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_udpmask_salamander_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_udpmask_salamander_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_udpmask_salamander_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_udpmask_salamander_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_udpmask_salamander_config_proto_rawDesc), len(file_transport_internet_udpmask_salamander_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_udpmask_salamander_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_udpmask_salamander_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_transport_internet_udpmask_salamander_config_proto_goTypes = []any{
|
||||
(*Config)(nil), // 0: xray.transport.internet.udpmask.salamander.Config
|
||||
}
|
||||
var file_transport_internet_udpmask_salamander_config_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_udpmask_salamander_config_proto_init() }
|
||||
func file_transport_internet_udpmask_salamander_config_proto_init() {
|
||||
if File_transport_internet_udpmask_salamander_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_udpmask_salamander_config_proto_rawDesc), len(file_transport_internet_udpmask_salamander_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_udpmask_salamander_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_udpmask_salamander_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_udpmask_salamander_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_udpmask_salamander_config_proto = out.File
|
||||
file_transport_internet_udpmask_salamander_config_proto_goTypes = nil
|
||||
file_transport_internet_udpmask_salamander_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.udpmask.salamander;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Udpmask.Salamander";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/udpmask/salamander";
|
||||
option java_package = "com.xray.transport.internet.udpmask.salamander";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Config {
|
||||
string password = 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package obfs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const udpBufferSize = 2048 // QUIC packets are at most 1500 bytes long, so 2k should be more than enough
|
||||
|
||||
// Obfuscator is the interface that wraps the Obfuscate and Deobfuscate methods.
|
||||
// Both methods return the number of bytes written to out.
|
||||
// If a packet is not valid, the methods should return 0.
|
||||
type Obfuscator interface {
|
||||
Obfuscate(in, out []byte) int
|
||||
Deobfuscate(in, out []byte) int
|
||||
}
|
||||
|
||||
var _ net.PacketConn = (*obfsPacketConn)(nil)
|
||||
|
||||
type obfsPacketConn struct {
|
||||
Conn net.PacketConn
|
||||
Obfs Obfuscator
|
||||
|
||||
readBuf []byte
|
||||
readMutex sync.Mutex
|
||||
writeBuf []byte
|
||||
writeMutex sync.Mutex
|
||||
}
|
||||
|
||||
// obfsPacketConnUDP is a special case of obfsPacketConn that uses a UDPConn
|
||||
// as the underlying connection. We pass additional methods to quic-go to
|
||||
// enable UDP-specific optimizations.
|
||||
type obfsPacketConnUDP struct {
|
||||
*obfsPacketConn
|
||||
UDPConn *net.UDPConn
|
||||
}
|
||||
|
||||
// WrapPacketConn enables obfuscation on a net.PacketConn.
|
||||
// The obfuscation is transparent to the caller - the n bytes returned by
|
||||
// ReadFrom and WriteTo are the number of original bytes, not after
|
||||
// obfuscation/deobfuscation.
|
||||
func WrapPacketConn(conn net.PacketConn, obfs Obfuscator) net.PacketConn {
|
||||
opc := &obfsPacketConn{
|
||||
Conn: conn,
|
||||
Obfs: obfs,
|
||||
readBuf: make([]byte, udpBufferSize),
|
||||
writeBuf: make([]byte, udpBufferSize),
|
||||
}
|
||||
if udpConn, ok := conn.(*net.UDPConn); ok {
|
||||
return &obfsPacketConnUDP{
|
||||
obfsPacketConn: opc,
|
||||
UDPConn: udpConn,
|
||||
}
|
||||
} else {
|
||||
return opc
|
||||
}
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
for {
|
||||
c.readMutex.Lock()
|
||||
n, addr, err = c.Conn.ReadFrom(c.readBuf)
|
||||
if n <= 0 {
|
||||
c.readMutex.Unlock()
|
||||
return n, addr, err
|
||||
}
|
||||
n = c.Obfs.Deobfuscate(c.readBuf[:n], p)
|
||||
c.readMutex.Unlock()
|
||||
if n > 0 || err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
// Invalid packet, try again
|
||||
}
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.writeMutex.Lock()
|
||||
nn := c.Obfs.Obfuscate(p, c.writeBuf)
|
||||
_, err = c.Conn.WriteTo(c.writeBuf[:nn], addr)
|
||||
c.writeMutex.Unlock()
|
||||
if err == nil {
|
||||
n = len(p)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) Close() error {
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) LocalAddr() net.Addr {
|
||||
return c.Conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) SetDeadline(t time.Time) error {
|
||||
return c.Conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) SetReadDeadline(t time.Time) error {
|
||||
return c.Conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *obfsPacketConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.Conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
// UDP-specific methods below
|
||||
|
||||
func (c *obfsPacketConnUDP) SetReadBuffer(bytes int) error {
|
||||
return c.UDPConn.SetReadBuffer(bytes)
|
||||
}
|
||||
|
||||
func (c *obfsPacketConnUDP) SetWriteBuffer(bytes int) error {
|
||||
return c.UDPConn.SetWriteBuffer(bytes)
|
||||
}
|
||||
|
||||
func (c *obfsPacketConnUDP) SyscallConn() (syscall.RawConn, error) {
|
||||
return c.UDPConn.SyscallConn()
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package obfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
const (
|
||||
smPSKMinLen = 4
|
||||
smSaltLen = 8
|
||||
smKeyLen = blake2b.Size256
|
||||
)
|
||||
|
||||
var _ Obfuscator = (*SalamanderObfuscator)(nil)
|
||||
|
||||
var ErrPSKTooShort = fmt.Errorf("PSK must be at least %d bytes", smPSKMinLen)
|
||||
|
||||
// SalamanderObfuscator is an obfuscator that obfuscates each packet with
|
||||
// the BLAKE2b-256 hash of a pre-shared key combined with a random salt.
|
||||
// Packet format: [8-byte salt][payload]
|
||||
type SalamanderObfuscator struct {
|
||||
PSK []byte
|
||||
RandSrc *rand.Rand
|
||||
|
||||
lk sync.Mutex
|
||||
}
|
||||
|
||||
func NewSalamanderObfuscator(psk []byte) (*SalamanderObfuscator, error) {
|
||||
if len(psk) < smPSKMinLen {
|
||||
return nil, ErrPSKTooShort
|
||||
}
|
||||
return &SalamanderObfuscator{
|
||||
PSK: psk,
|
||||
RandSrc: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *SalamanderObfuscator) Obfuscate(in, out []byte) int {
|
||||
outLen := len(in) + smSaltLen
|
||||
if len(out) < outLen {
|
||||
return 0
|
||||
}
|
||||
o.lk.Lock()
|
||||
_, _ = o.RandSrc.Read(out[:smSaltLen])
|
||||
o.lk.Unlock()
|
||||
key := o.key(out[:smSaltLen])
|
||||
for i, c := range in {
|
||||
out[i+smSaltLen] = c ^ key[i%smKeyLen]
|
||||
}
|
||||
return outLen
|
||||
}
|
||||
|
||||
func (o *SalamanderObfuscator) Deobfuscate(in, out []byte) int {
|
||||
outLen := len(in) - smSaltLen
|
||||
if outLen <= 0 || len(out) < outLen {
|
||||
return 0
|
||||
}
|
||||
key := o.key(in[:smSaltLen])
|
||||
for i, c := range in[smSaltLen:] {
|
||||
out[i] = c ^ key[i%smKeyLen]
|
||||
}
|
||||
return outLen
|
||||
}
|
||||
|
||||
func (o *SalamanderObfuscator) key(salt []byte) [smKeyLen]byte {
|
||||
return blake2b.Sum256(append(o.PSK, salt...))
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package obfs
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func BenchmarkSalamanderObfuscator_Obfuscate(b *testing.B) {
|
||||
o, _ := NewSalamanderObfuscator([]byte("average_password"))
|
||||
in := make([]byte, 1200)
|
||||
_, _ = rand.Read(in)
|
||||
out := make([]byte, 2048)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
o.Obfuscate(in, out)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSalamanderObfuscator_Deobfuscate(b *testing.B) {
|
||||
o, _ := NewSalamanderObfuscator([]byte("average_password"))
|
||||
in := make([]byte, 1200)
|
||||
_, _ = rand.Read(in)
|
||||
out := make([]byte, 2048)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
o.Deobfuscate(in, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSalamanderObfuscator(t *testing.T) {
|
||||
o, _ := NewSalamanderObfuscator([]byte("average_password"))
|
||||
in := make([]byte, 1200)
|
||||
oOut := make([]byte, 2048)
|
||||
dOut := make([]byte, 2048)
|
||||
for i := 0; i < 1000; i++ {
|
||||
_, _ = rand.Read(in)
|
||||
n := o.Obfuscate(in, oOut)
|
||||
assert.Equal(t, len(in)+smSaltLen, n)
|
||||
n = o.Deobfuscate(oOut[:n], dOut)
|
||||
assert.Equal(t, len(in), n)
|
||||
assert.Equal(t, in, dOut[:n])
|
||||
}
|
||||
}
|
||||
@@ -182,6 +182,8 @@ func Test_listenHTTPUpgradeAndDial_TLS(t *testing.T) {
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
streamSettings := &internet.MemoryStreamConfig{
|
||||
ProtocolName: "httpupgrade",
|
||||
ProtocolSettings: &Config{
|
||||
@@ -189,8 +191,8 @@ func Test_listenHTTPUpgradeAndDial_TLS(t *testing.T) {
|
||||
},
|
||||
SecurityType: "tls",
|
||||
SecuritySettings: &tls.Config{
|
||||
AllowInsecure: true,
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))},
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(ct)},
|
||||
PinnedPeerCertSha256: [][]byte{ctHash[:]},
|
||||
},
|
||||
}
|
||||
listen, err := ListenHTTPUpgrade(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package hysteria
|
||||
|
||||
import (
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/padding"
|
||||
)
|
||||
|
||||
const (
|
||||
closeErrCodeOK = 0x100 // HTTP3 ErrCodeNoError
|
||||
closeErrCodeProtocolError = 0x101 // HTTP3 ErrCodeGeneralProtocolError
|
||||
|
||||
MaxDatagramFrameSize = 1200
|
||||
|
||||
URLHost = "hysteria"
|
||||
URLPath = "/auth"
|
||||
|
||||
RequestHeaderAuth = "Hysteria-Auth"
|
||||
ResponseHeaderUDPEnabled = "Hysteria-UDP"
|
||||
CommonHeaderCCRX = "Hysteria-CC-RX"
|
||||
CommonHeaderPadding = "Hysteria-Padding"
|
||||
|
||||
StatusAuthOK = 233
|
||||
|
||||
udpMessageChanSize = 1024
|
||||
)
|
||||
|
||||
var (
|
||||
authRequestPadding = padding.Padding{Min: 256, Max: 2048}
|
||||
// authResponsePadding = padding.Padding{Min: 256, Max: 2048}
|
||||
)
|
||||
|
||||
type Status int
|
||||
|
||||
const (
|
||||
StatusUnknown Status = iota
|
||||
StatusActive
|
||||
StatusInactive
|
||||
)
|
||||
|
||||
const protocolName = "hysteria"
|
||||
|
||||
func init() {
|
||||
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
|
||||
return new(Config)
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.10
|
||||
// protoc v6.33.1
|
||||
// source: transport/internet/hysteria/config.proto
|
||||
|
||||
package hysteria
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
|
||||
Auth string `protobuf:"bytes,2,opt,name=auth,proto3" json:"auth,omitempty"`
|
||||
Congestion string `protobuf:"bytes,3,opt,name=congestion,proto3" json:"congestion,omitempty"`
|
||||
Up uint64 `protobuf:"varint,4,opt,name=up,proto3" json:"up,omitempty"`
|
||||
Down uint64 `protobuf:"varint,5,opt,name=down,proto3" json:"down,omitempty"`
|
||||
Ports []uint32 `protobuf:"varint,6,rep,packed,name=ports,proto3" json:"ports,omitempty"`
|
||||
IntervalMin int64 `protobuf:"varint,7,opt,name=interval_min,json=intervalMin,proto3" json:"interval_min,omitempty"`
|
||||
IntervalMax int64 `protobuf:"varint,8,opt,name=interval_max,json=intervalMax,proto3" json:"interval_max,omitempty"`
|
||||
InitStreamReceiveWindow uint64 `protobuf:"varint,9,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"`
|
||||
MaxStreamReceiveWindow uint64 `protobuf:"varint,10,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"`
|
||||
InitConnReceiveWindow uint64 `protobuf:"varint,11,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"`
|
||||
MaxConnReceiveWindow uint64 `protobuf:"varint,12,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"`
|
||||
MaxIdleTimeout int64 `protobuf:"varint,13,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"`
|
||||
KeepAlivePeriod int64 `protobuf:"varint,14,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"`
|
||||
DisablePathMtuDiscovery bool `protobuf:"varint,15,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_hysteria_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_hysteria_config_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_hysteria_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetVersion() int32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetAuth() string {
|
||||
if x != nil {
|
||||
return x.Auth
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetCongestion() string {
|
||||
if x != nil {
|
||||
return x.Congestion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetUp() uint64 {
|
||||
if x != nil {
|
||||
return x.Up
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetDown() uint64 {
|
||||
if x != nil {
|
||||
return x.Down
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetPorts() []uint32 {
|
||||
if x != nil {
|
||||
return x.Ports
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetIntervalMin() int64 {
|
||||
if x != nil {
|
||||
return x.IntervalMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetIntervalMax() int64 {
|
||||
if x != nil {
|
||||
return x.IntervalMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetInitStreamReceiveWindow() uint64 {
|
||||
if x != nil {
|
||||
return x.InitStreamReceiveWindow
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetMaxStreamReceiveWindow() uint64 {
|
||||
if x != nil {
|
||||
return x.MaxStreamReceiveWindow
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetInitConnReceiveWindow() uint64 {
|
||||
if x != nil {
|
||||
return x.InitConnReceiveWindow
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetMaxConnReceiveWindow() uint64 {
|
||||
if x != nil {
|
||||
return x.MaxConnReceiveWindow
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetMaxIdleTimeout() int64 {
|
||||
if x != nil {
|
||||
return x.MaxIdleTimeout
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetKeepAlivePeriod() int64 {
|
||||
if x != nil {
|
||||
return x.KeepAlivePeriod
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetDisablePathMtuDiscovery() bool {
|
||||
if x != nil {
|
||||
return x.DisablePathMtuDiscovery
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_transport_internet_hysteria_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_hysteria_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\xd1\x04\n" +
|
||||
"\x06Config\x12\x18\n" +
|
||||
"\aversion\x18\x01 \x01(\x05R\aversion\x12\x12\n" +
|
||||
"\x04auth\x18\x02 \x01(\tR\x04auth\x12\x1e\n" +
|
||||
"\n" +
|
||||
"congestion\x18\x03 \x01(\tR\n" +
|
||||
"congestion\x12\x0e\n" +
|
||||
"\x02up\x18\x04 \x01(\x04R\x02up\x12\x12\n" +
|
||||
"\x04down\x18\x05 \x01(\x04R\x04down\x12\x14\n" +
|
||||
"\x05ports\x18\x06 \x03(\rR\x05ports\x12!\n" +
|
||||
"\finterval_min\x18\a \x01(\x03R\vintervalMin\x12!\n" +
|
||||
"\finterval_max\x18\b \x01(\x03R\vintervalMax\x12;\n" +
|
||||
"\x1ainit_stream_receive_window\x18\t \x01(\x04R\x17initStreamReceiveWindow\x129\n" +
|
||||
"\x19max_stream_receive_window\x18\n" +
|
||||
" \x01(\x04R\x16maxStreamReceiveWindow\x127\n" +
|
||||
"\x18init_conn_receive_window\x18\v \x01(\x04R\x15initConnReceiveWindow\x125\n" +
|
||||
"\x17max_conn_receive_window\x18\f \x01(\x04R\x14maxConnReceiveWindow\x12(\n" +
|
||||
"\x10max_idle_timeout\x18\r \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
|
||||
"\x11keep_alive_period\x18\x0e \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
|
||||
"\x1adisable_path_mtu_discovery\x18\x0f \x01(\bR\x17disablePathMtuDiscoveryB\x82\x01\n" +
|
||||
"$com.xray.transport.internet.hysteriaP\x01Z5github.com/xtls/xray-core/transport/internet/hysteria\xaa\x02 Xray.Transport.Internet.Hysteriab\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_hysteria_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_hysteria_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_hysteria_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_hysteria_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_hysteria_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_hysteria_config_proto_rawDesc), len(file_transport_internet_hysteria_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_hysteria_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_hysteria_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_transport_internet_hysteria_config_proto_goTypes = []any{
|
||||
(*Config)(nil), // 0: xray.transport.internet.hysteria.Config
|
||||
}
|
||||
var file_transport_internet_hysteria_config_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_hysteria_config_proto_init() }
|
||||
func file_transport_internet_hysteria_config_proto_init() {
|
||||
if File_transport_internet_hysteria_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_hysteria_config_proto_rawDesc), len(file_transport_internet_hysteria_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_hysteria_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_hysteria_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_hysteria_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_hysteria_config_proto = out.File
|
||||
file_transport_internet_hysteria_config_proto_goTypes = nil
|
||||
file_transport_internet_hysteria_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.hysteria;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Hysteria";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/hysteria";
|
||||
option java_package = "com.xray.transport.internet.hysteria";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Config {
|
||||
int32 version = 1;
|
||||
string auth = 2;
|
||||
string congestion = 3;
|
||||
uint64 up = 4;
|
||||
uint64 down = 5;
|
||||
repeated uint32 ports = 6;
|
||||
int64 interval_min = 7;
|
||||
int64 interval_max = 8;
|
||||
|
||||
uint64 init_stream_receive_window = 9;
|
||||
uint64 max_stream_receive_window = 10;
|
||||
uint64 init_conn_receive_window = 11;
|
||||
uint64 max_conn_receive_window = 12;
|
||||
int64 max_idle_timeout = 13;
|
||||
int64 keep_alive_period = 14;
|
||||
bool disable_path_mtu_discovery = 15;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user