Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 37efc4237a | |||
| 2c92339f95 | |||
| 9c46a2d55a | |||
| 19186edfa1 | |||
| f6a7e93923 | |||
| 077070dbe0 | |||
| c3af657c0e | |||
| 9a04eecaf9 | |||
| 1951a278ac | |||
| f1aee0b7c5 | |||
| 5173e5c15d | |||
| daf9cba29f | |||
| 445c0d456c |
@@ -138,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)
|
||||
|
||||
+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, "]")
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package router
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -78,6 +79,8 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
rr.Geoip = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
if len(rr.SourceGeoip) > 0 {
|
||||
@@ -86,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 {
|
||||
@@ -95,6 +100,8 @@ 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 {
|
||||
@@ -104,6 +111,8 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||
}
|
||||
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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -29,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)
|
||||
|
||||
@@ -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{} {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,13 +11,11 @@ require (
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pires/go-proxyproto v0.9.0
|
||||
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
|
||||
@@ -30,7 +28,7 @@ require (
|
||||
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-20260109181451-4be7c433dae2
|
||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
||||
h12.io/socks v1.0.3
|
||||
lukechampine.com/blake3 v1.4.1
|
||||
)
|
||||
@@ -38,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
|
||||
@@ -46,7 +43,6 @@ 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.31.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
|
||||
@@ -5,11 +5,8 @@ github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178/go.mod h1:N1WIj
|
||||
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=
|
||||
@@ -46,30 +43,22 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
|
||||
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.9.0 h1:3Qg3CLxWx4wJOw5uxhTvc0VrgsJeerDbGTvexu4UK1E=
|
||||
github.com/pires/go-proxyproto v0.9.0/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/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/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/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=
|
||||
@@ -154,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-20260109181451-4be7c433dae2 h1:fr6L00yGG2RP5NMea6njWpdC+bm+cMdFClrSpaicp1c=
|
||||
gvisor.dev/gvisor v0.0.0-20260109181451-4be7c433dae2/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q=
|
||||
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
-1
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
+88
-91
@@ -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,125 +176,119 @@ 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 {
|
||||
bs, err := loadFile(file)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to load file: ", file).Base(err)
|
||||
}
|
||||
bs = find(bs, []byte(code))
|
||||
if bs == nil {
|
||||
return nil, errors.New("code not found in ", file, ": ", code)
|
||||
}
|
||||
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 // 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 {
|
||||
bs, err := loadFile(file)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to load file: ", file).Base(err)
|
||||
}
|
||||
bs = find(bs, []byte(code))
|
||||
if bs == nil {
|
||||
return nil, errors.New("list not found in ", file, ": ", code)
|
||||
}
|
||||
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 // do not cache geosite
|
||||
SiteCache[index] = &geosite
|
||||
bs, err := loadFile(file, code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return SiteCache[index].Domain, nil
|
||||
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
|
||||
}
|
||||
|
||||
func DecodeVarint(buf []byte) (x uint64, n int) {
|
||||
func decodeVarint(r *bufio.Reader) (uint64, error) {
|
||||
var x uint64
|
||||
for shift := uint(0); shift < 64; shift += 7 {
|
||||
if n >= len(buf) {
|
||||
return 0, 0
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b := uint64(buf[n])
|
||||
n++
|
||||
x |= (b & 0x7F) << shift
|
||||
x |= (uint64(b) & 0x7F) << shift
|
||||
if (b & 0x80) == 0 {
|
||||
return x, n
|
||||
return x, nil
|
||||
}
|
||||
}
|
||||
|
||||
// The number is too large to represent in a 64-bit value.
|
||||
return 0, 0
|
||||
return 0, errors.New("varint overflow")
|
||||
}
|
||||
|
||||
func find(data, code []byte) []byte {
|
||||
func find(r io.Reader, code []byte) []byte {
|
||||
codeL := len(code)
|
||||
if codeL == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
br := bufio.NewReaderSize(r, 64*1024)
|
||||
need := 2 + codeL
|
||||
prefixBuf := make([]byte, need)
|
||||
|
||||
for {
|
||||
dataL := len(data)
|
||||
if dataL < 2 {
|
||||
if _, err := br.ReadByte(); err != nil {
|
||||
return nil
|
||||
}
|
||||
x, y := DecodeVarint(data[1:])
|
||||
if x == 0 && y == 0 {
|
||||
|
||||
x, err := decodeVarint(br)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
headL, bodyL := 1+y, int(x)
|
||||
dataL -= headL
|
||||
if dataL < bodyL {
|
||||
bodyL := int(x)
|
||||
if bodyL <= 0 {
|
||||
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]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
if dataL == bodyL {
|
||||
return nil
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
data = data[bodyL:]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,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)
|
||||
}
|
||||
@@ -501,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)
|
||||
}
|
||||
@@ -655,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,7 +46,6 @@ 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) {
|
||||
@@ -64,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.")
|
||||
@@ -83,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.")
|
||||
@@ -168,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"`
|
||||
}
|
||||
@@ -180,7 +176,6 @@ 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"`
|
||||
@@ -198,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,
|
||||
},
|
||||
@@ -254,8 +248,6 @@ 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),
|
||||
|
||||
@@ -387,7 +387,7 @@ func (b Bandwidth) Bps() (uint64, error) {
|
||||
|
||||
type UdpHop struct {
|
||||
PortList json.RawMessage `json:"port"`
|
||||
Interval int64 `json:"interval"`
|
||||
Interval *Int32Range `json:"interval"`
|
||||
}
|
||||
|
||||
type HysteriaConfig struct {
|
||||
@@ -431,13 +431,19 @@ func (c *HysteriaConfig) Build() (proto.Message, error) {
|
||||
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 c.UdpHop.Interval != 0 && c.UdpHop.Interval < 5 {
|
||||
if (inertvalMin != 0 && inertvalMin < 5) || (inertvalMax != 0 && inertvalMax < 5) {
|
||||
return nil, errors.New("Interval must be at least 5")
|
||||
}
|
||||
|
||||
@@ -467,7 +473,8 @@ func (c *HysteriaConfig) Build() (proto.Message, error) {
|
||||
config.Up = up
|
||||
config.Down = down
|
||||
config.Ports = hop.Build().Ports()
|
||||
config.Interval = c.UdpHop.Interval
|
||||
config.IntervalMin = inertvalMin
|
||||
config.IntervalMax = inertvalMax
|
||||
config.InitStreamReceiveWindow = c.InitStreamReceiveWindow
|
||||
config.MaxStreamReceiveWindow = c.MaxStreamReceiveWindow
|
||||
config.InitConnReceiveWindow = c.InitConnectionReceiveWindow
|
||||
@@ -561,7 +568,7 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
Insecure bool `json:"allowInsecure"`
|
||||
AllowInsecure bool `json:"allowInsecure"`
|
||||
Certs []*TLSCertConfig `json:"certificates"`
|
||||
ServerName string `json:"serverName"`
|
||||
ALPN *StringList `json:"alpn"`
|
||||
@@ -572,10 +579,10 @@ type TLSConfig struct {
|
||||
CipherSuites string `json:"cipherSuites"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
RejectUnknownSNI bool `json:"rejectUnknownSni"`
|
||||
PinnedPeerCertSha256 string `json:"pinnedPeerCertSha256"`
|
||||
CurvePreferences *StringList `json:"curvePreferences"`
|
||||
MasterKeyLog string `json:"masterKeyLog"`
|
||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||
PinnedPeerCertSha256 string `json:"pinnedPeerCertSha256"`
|
||||
VerifyPeerCertByName string `json:"verifyPeerCertByName"`
|
||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||
ECHServerKeys string `json:"echServerKeys"`
|
||||
ECHConfigList string `json:"echConfigList"`
|
||||
@@ -595,10 +602,6 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
config.Certificate[idx] = cert
|
||||
}
|
||||
serverName := c.ServerName
|
||||
config.AllowInsecure = c.Insecure
|
||||
if config.AllowInsecure {
|
||||
errors.PrintDeprecatedFeatureWarning("allowInsecure", "pinnedPeerCertSha256")
|
||||
}
|
||||
if len(c.ServerName) > 0 {
|
||||
config.ServerName = serverName
|
||||
}
|
||||
@@ -625,12 +628,13 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New(`unknown "fingerprint": `, config.Fingerprint)
|
||||
}
|
||||
config.RejectUnknownSni = c.RejectUnknownSNI
|
||||
config.MasterKeyLog = c.MasterKeyLog
|
||||
|
||||
if c.AllowInsecure {
|
||||
return nil, errors.PrintRemovedFeatureError(`"allowInsecure"`, `"pinnedPeerCertSha256"`)
|
||||
}
|
||||
if c.PinnedPeerCertSha256 != "" {
|
||||
config.PinnedPeerCertSha256 = [][]byte{}
|
||||
// Split by tilde separator
|
||||
hashes := strings.Split(c.PinnedPeerCertSha256, "~")
|
||||
for _, v := range hashes {
|
||||
for v := range strings.SplitSeq(c.PinnedPeerCertSha256, ",") {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
continue
|
||||
@@ -643,12 +647,18 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
}
|
||||
|
||||
config.MasterKeyLog = c.MasterKeyLog
|
||||
|
||||
if c.ServerNameToVerify != "" {
|
||||
return nil, errors.PrintRemovedFeatureError(`"serverNameToVerify"`, `"verifyPeerCertInNames"`)
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -236,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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+260
-75
@@ -5,163 +5,348 @@ package tun
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"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"
|
||||
utunOptIfName = 2
|
||||
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 {
|
||||
tunFd int
|
||||
name string
|
||||
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) {
|
||||
tunFd, name, err := openUTun(options.Name)
|
||||
// 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{
|
||||
tunFd: tunFd,
|
||||
name: name,
|
||||
tunFile: tunFile,
|
||||
options: options,
|
||||
ownsFd: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *DarwinTun) Start() error {
|
||||
if t.options.MTU > 0 {
|
||||
if err := setMTU(t.name, int(t.options.MTU)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return setState(t.name, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DarwinTun) Close() error {
|
||||
_ = setState(t.name, false)
|
||||
return unix.Close(t.tunFd)
|
||||
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 newDarwinEndpoint(t.tunFd, t.options.MTU), nil
|
||||
return &LinkEndpoint{deviceMTU: t.options.MTU, device: t}, nil
|
||||
}
|
||||
|
||||
func openUTun(name string) (int, string, error) {
|
||||
// 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 -1, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctlInfo := &unix.CtlInfo{}
|
||||
copy(ctlInfo.Name[:], utunControlName)
|
||||
if err := unix.IoctlCtlInfo(fd, ctlInfo); err != nil {
|
||||
_ = unix.Close(fd)
|
||||
return -1, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sockaddr := &unix.SockaddrCtl{
|
||||
ID: ctlInfo.Id,
|
||||
Unit: parseUTunUnit(name),
|
||||
Unit: uint32(ifIndex) + 1,
|
||||
}
|
||||
|
||||
if err := unix.Connect(fd, sockaddr); err != nil {
|
||||
_ = unix.Close(fd)
|
||||
return -1, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := unix.SetNonblock(fd, true); err != nil {
|
||||
_ = unix.Close(fd)
|
||||
return -1, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tunName, err := unix.GetsockoptString(fd, sysprotoControl, utunOptIfName)
|
||||
if err != nil {
|
||||
_ = unix.Close(fd)
|
||||
return -1, "", err
|
||||
}
|
||||
|
||||
tunName = strings.TrimRight(tunName, "\x00")
|
||||
if tunName == "" {
|
||||
_ = unix.Close(fd)
|
||||
return -1, "", errors.New("empty utun name")
|
||||
}
|
||||
|
||||
return fd, tunName, nil
|
||||
return os.NewFile(uintptr(fd), name), nil
|
||||
}
|
||||
|
||||
func parseUTunUnit(name string) uint32 {
|
||||
var unit uint32
|
||||
if _, err := fmt.Sscanf(name, "utun%d", &unit); err != nil {
|
||||
return 0
|
||||
}
|
||||
return unit + 1
|
||||
}
|
||||
|
||||
type ifreqMTU struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
MTU int32
|
||||
_ [12]byte
|
||||
}
|
||||
|
||||
type ifreqFlags struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Flags int16
|
||||
_ [14]byte
|
||||
}
|
||||
|
||||
func setMTU(name string, mtu int) error {
|
||||
if mtu <= 0 {
|
||||
return nil
|
||||
// setup the interface by name
|
||||
func setup(name string, MTU uint32) error {
|
||||
if err := setMTU(name, MTU); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
/*
|
||||
* 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 func() { _ = unix.Close(fd) }()
|
||||
defer unix.Close(socket)
|
||||
|
||||
ifr := ifreqMTU{MTU: int32(mtu)}
|
||||
ifr := unix.IfreqMTU{MTU: int32(mtu)}
|
||||
copy(ifr.Name[:], name)
|
||||
return ioctlPtr(fd, unix.SIOCSIFMTU, unsafe.Pointer(&ifr))
|
||||
return unix.IoctlSetIfreqMTU(socket, &ifr)
|
||||
}
|
||||
|
||||
func setState(name string, up bool) error {
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
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 func() { _ = unix.Close(fd) }()
|
||||
defer unix.Close(socket4)
|
||||
|
||||
ifr := ifreqFlags{}
|
||||
copy(ifr.Name[:], name)
|
||||
// assume local ip address is next one from the remote address
|
||||
local4 := gateway.Addr().As4()
|
||||
local4[3]++
|
||||
|
||||
if err := ioctlPtr(fd, unix.SIOCGIFFLAGS, unsafe.Pointer(&ifr)); err != nil {
|
||||
// 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)
|
||||
|
||||
if up {
|
||||
ifr.Flags |= unix.IFF_UP
|
||||
} else {
|
||||
ifr.Flags &^= unix.IFF_UP
|
||||
// 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 ioctlPtr(fd, unix.SIOCSIFFLAGS, unsafe.Pointer(&ifr))
|
||||
return nil
|
||||
}
|
||||
|
||||
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
|
||||
_, _, errno := unix.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
//go:build darwin
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
const utunHeaderSize = 4
|
||||
|
||||
var ErrUnsupportedNetworkProtocol = errors.New("unsupported ip version")
|
||||
|
||||
// DarwinEndpoint implements GVisor stack.LinkEndpoint
|
||||
var _ stack.LinkEndpoint = (*DarwinEndpoint)(nil)
|
||||
|
||||
type DarwinEndpoint struct {
|
||||
tunFd int
|
||||
mtu uint32
|
||||
dispatcherCancel context.CancelFunc
|
||||
}
|
||||
|
||||
func newDarwinEndpoint(tunFd int, mtu uint32) *DarwinEndpoint {
|
||||
return &DarwinEndpoint{
|
||||
tunFd: tunFd,
|
||||
mtu: mtu,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) MTU() uint32 {
|
||||
return e.mtu
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) SetMTU(_ uint32) {
|
||||
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) MaxHeaderLength() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) LinkAddress() tcpip.LinkAddress {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) SetLinkAddress(_ tcpip.LinkAddress) {
|
||||
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||
return stack.CapabilityRXChecksumOffload
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) 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 *DarwinEndpoint) IsAttached() bool {
|
||||
return e.dispatcherCancel != nil
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) Wait() {
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
return header.ARPHardwareNone
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) AddHeader(buffer *stack.PacketBuffer) {
|
||||
// tun interface doesn't have link layer header, it will be added by the OS
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) ParseHeader(ptr *stack.PacketBuffer) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) Close() {
|
||||
if e.dispatcherCancel != nil {
|
||||
e.dispatcherCancel()
|
||||
e.dispatcherCancel = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) SetOnCloseAction(_ func()) {
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
||||
var n int
|
||||
for _, packetBuffer := range packetBufferList.AsSlice() {
|
||||
family, err := ipFamilyFromPacket(packetBuffer)
|
||||
if err != nil {
|
||||
return n, &tcpip.ErrAborted{}
|
||||
}
|
||||
|
||||
var headerBytes [utunHeaderSize]byte
|
||||
binary.BigEndian.PutUint32(headerBytes[:], family)
|
||||
|
||||
writeSlices := append([][]byte{headerBytes[:]}, packetBuffer.AsSlices()...)
|
||||
if _, err := unix.Writev(e.tunFd, writeSlices); err != nil {
|
||||
if errors.Is(err, unix.EAGAIN) {
|
||||
return n, &tcpip.ErrWouldBlock{}
|
||||
}
|
||||
return n, &tcpip.ErrAborted{}
|
||||
}
|
||||
n++
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) dispatchLoop(ctx context.Context, dispatcher stack.NetworkDispatcher) {
|
||||
readSize := int(e.mtu)
|
||||
if readSize <= 0 {
|
||||
readSize = 65535
|
||||
}
|
||||
readSize += utunHeaderSize
|
||||
|
||||
buf := make([]byte, readSize)
|
||||
for ctx.Err() == nil {
|
||||
|
||||
n, err := unix.Read(e.tunFd, buf)
|
||||
if err != nil {
|
||||
if errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EINTR) {
|
||||
continue
|
||||
}
|
||||
e.Attach(nil)
|
||||
return
|
||||
}
|
||||
if n <= utunHeaderSize {
|
||||
continue
|
||||
}
|
||||
|
||||
networkProtocol, packet, err := parseUTunPacket(buf[:n])
|
||||
if errors.Is(err, ErrUnsupportedNetworkProtocol) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
e.Attach(nil)
|
||||
return
|
||||
}
|
||||
|
||||
dispatcher.DeliverNetworkPacket(networkProtocol, packet)
|
||||
packet.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
func parseUTunPacket(packet []byte) (tcpip.NetworkProtocolNumber, *stack.PacketBuffer, error) {
|
||||
if len(packet) <= utunHeaderSize {
|
||||
return 0, nil, errors.New("packet too short")
|
||||
}
|
||||
|
||||
family := binary.BigEndian.Uint32(packet[:utunHeaderSize])
|
||||
var networkProtocol tcpip.NetworkProtocolNumber
|
||||
switch family {
|
||||
case uint32(unix.AF_INET):
|
||||
networkProtocol = header.IPv4ProtocolNumber
|
||||
case uint32(unix.AF_INET6):
|
||||
networkProtocol = header.IPv6ProtocolNumber
|
||||
default:
|
||||
return 0, nil, ErrUnsupportedNetworkProtocol
|
||||
}
|
||||
|
||||
payload := packet[utunHeaderSize:]
|
||||
packetBuffer := buffer.MakeWithData(payload)
|
||||
return networkProtocol, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: packetBuffer,
|
||||
IsForwardedPacket: true,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func ipFamilyFromPacket(packetBuffer *stack.PacketBuffer) (uint32, error) {
|
||||
for _, slice := range packetBuffer.AsSlices() {
|
||||
if len(slice) == 0 {
|
||||
continue
|
||||
}
|
||||
switch header.IPVersion(slice) {
|
||||
case header.IPv4Version:
|
||||
return uint32(unix.AF_INET), nil
|
||||
case header.IPv6Version:
|
||||
return uint32(unix.AF_INET6), nil
|
||||
default:
|
||||
return 0, ErrUnsupportedNetworkProtocol
|
||||
}
|
||||
}
|
||||
return 0, errors.New("empty packet")
|
||||
}
|
||||
+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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -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[:]},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -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)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -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)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -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)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -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)},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -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,7 +682,7 @@ 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.GenerateCertHash(certificateDer.Certificate)
|
||||
userID := protocol.NewID(uuid.New())
|
||||
@@ -743,7 +753,6 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
@@ -769,7 +778,7 @@ 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.GenerateCertHash(certificateDer.Certificate)
|
||||
certHash[1] += 1
|
||||
@@ -841,7 +850,6 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
@@ -867,7 +875,7 @@ 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.GenerateCertHash(certificateDer.Certificate)
|
||||
userID := protocol.NewID(uuid.New())
|
||||
@@ -939,7 +947,6 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Fingerprint: "random",
|
||||
AllowInsecure: true,
|
||||
PinnedPeerCertSha256: [][]byte{certHash},
|
||||
}),
|
||||
},
|
||||
@@ -965,7 +972,7 @@ 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.GenerateCertHash(certificateDer.Certificate)
|
||||
certHash[1] += 1
|
||||
@@ -1038,7 +1045,6 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
Fingerprint: "random",
|
||||
AllowInsecure: true,
|
||||
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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -29,14 +29,15 @@ type Config struct {
|
||||
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"`
|
||||
Interval int64 `protobuf:"varint,7,opt,name=interval,proto3" json:"interval,omitempty"`
|
||||
InitStreamReceiveWindow uint64 `protobuf:"varint,8,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"`
|
||||
MaxStreamReceiveWindow uint64 `protobuf:"varint,9,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"`
|
||||
InitConnReceiveWindow uint64 `protobuf:"varint,10,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"`
|
||||
MaxConnReceiveWindow uint64 `protobuf:"varint,11,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"`
|
||||
MaxIdleTimeout int64 `protobuf:"varint,12,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"`
|
||||
KeepAlivePeriod int64 `protobuf:"varint,13,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"`
|
||||
DisablePathMtuDiscovery bool `protobuf:"varint,14,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,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
|
||||
}
|
||||
@@ -113,9 +114,16 @@ func (x *Config) GetPorts() []uint32 {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetInterval() int64 {
|
||||
func (x *Config) GetIntervalMin() int64 {
|
||||
if x != nil {
|
||||
return x.Interval
|
||||
return x.IntervalMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetIntervalMax() int64 {
|
||||
if x != nil {
|
||||
return x.IntervalMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -173,7 +181,7 @@ 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\"\xa7\x04\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" +
|
||||
@@ -182,16 +190,17 @@ const file_transport_internet_hysteria_config_proto_rawDesc = "" +
|
||||
"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\x1a\n" +
|
||||
"\binterval\x18\a \x01(\x03R\binterval\x12;\n" +
|
||||
"\x1ainit_stream_receive_window\x18\b \x01(\x04R\x17initStreamReceiveWindow\x129\n" +
|
||||
"\x19max_stream_receive_window\x18\t \x01(\x04R\x16maxStreamReceiveWindow\x127\n" +
|
||||
"\x18init_conn_receive_window\x18\n" +
|
||||
" \x01(\x04R\x15initConnReceiveWindow\x125\n" +
|
||||
"\x17max_conn_receive_window\x18\v \x01(\x04R\x14maxConnReceiveWindow\x12(\n" +
|
||||
"\x10max_idle_timeout\x18\f \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
|
||||
"\x11keep_alive_period\x18\r \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
|
||||
"\x1adisable_path_mtu_discovery\x18\x0e \x01(\bR\x17disablePathMtuDiscoveryB\x82\x01\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 (
|
||||
|
||||
@@ -13,14 +13,15 @@ message Config {
|
||||
uint64 up = 4;
|
||||
uint64 down = 5;
|
||||
repeated uint32 ports = 6;
|
||||
int64 interval = 7;
|
||||
int64 interval_min = 7;
|
||||
int64 interval_max = 8;
|
||||
|
||||
uint64 init_stream_receive_window = 8;
|
||||
uint64 max_stream_receive_window = 9;
|
||||
uint64 init_conn_receive_window = 10;
|
||||
uint64 max_conn_receive_window = 11;
|
||||
int64 max_idle_timeout = 12;
|
||||
int64 keep_alive_period = 13;
|
||||
bool disable_path_mtu_discovery = 14;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ func (c *client) dial() error {
|
||||
IP: remote.(*net.UDPAddr).IP,
|
||||
Ports: c.config.Ports,
|
||||
}
|
||||
pktConn, err = udphop.NewUDPHopPacketConn(addr, time.Duration(c.config.Interval)*time.Second, c.udphopDialer, pktConn, index)
|
||||
pktConn, err = udphop.NewUDPHopPacketConn(addr, c.config.IntervalMin, c.config.IntervalMax, c.udphopDialer, pktConn, index)
|
||||
if err != nil {
|
||||
return errors.New("udphop err").Base(err)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -17,10 +19,11 @@ const (
|
||||
)
|
||||
|
||||
type udpHopPacketConn struct {
|
||||
Addr net.Addr
|
||||
Addrs []net.Addr
|
||||
HopInterval time.Duration
|
||||
ListenUDPFunc ListenUDPFunc
|
||||
Addr net.Addr
|
||||
Addrs []net.Addr
|
||||
HopIntervalMin int64
|
||||
HopIntervalMax int64
|
||||
ListenUDPFunc ListenUDPFunc
|
||||
|
||||
connMutex sync.RWMutex
|
||||
prevConn net.PacketConn
|
||||
@@ -46,10 +49,12 @@ type udpPacket struct {
|
||||
|
||||
type ListenUDPFunc = func(*net.UDPAddr) (net.PacketConn, error)
|
||||
|
||||
func NewUDPHopPacketConn(addr *UDPHopAddr, hopInterval time.Duration, listenUDPFunc ListenUDPFunc, pktConn net.PacketConn, index int) (net.PacketConn, error) {
|
||||
if hopInterval == 0 {
|
||||
hopInterval = defaultHopInterval
|
||||
} else if hopInterval < 5*time.Second {
|
||||
func NewUDPHopPacketConn(addr *UDPHopAddr, intervalMin int64, intervalMax int64, listenUDPFunc ListenUDPFunc, pktConn net.PacketConn, index int) (net.PacketConn, error) {
|
||||
if intervalMin == 0 || intervalMax == 0 {
|
||||
intervalMin = int64(defaultHopInterval)
|
||||
intervalMax = int64(defaultHopInterval)
|
||||
}
|
||||
if intervalMin < 5 || intervalMax < 5 {
|
||||
return nil, errors.New("hop interval must be at least 5 seconds")
|
||||
}
|
||||
// if listenUDPFunc == nil {
|
||||
@@ -69,15 +74,16 @@ func NewUDPHopPacketConn(addr *UDPHopAddr, hopInterval time.Duration, listenUDPF
|
||||
// return nil, err
|
||||
// }
|
||||
hConn := &udpHopPacketConn{
|
||||
Addr: addr,
|
||||
Addrs: addrs,
|
||||
HopInterval: hopInterval,
|
||||
ListenUDPFunc: listenUDPFunc,
|
||||
prevConn: nil,
|
||||
currentConn: pktConn,
|
||||
addrIndex: index,
|
||||
recvQueue: make(chan *udpPacket, packetQueueSize),
|
||||
closeChan: make(chan struct{}),
|
||||
Addr: addr,
|
||||
Addrs: addrs,
|
||||
HopIntervalMin: intervalMin,
|
||||
HopIntervalMax: intervalMax,
|
||||
ListenUDPFunc: listenUDPFunc,
|
||||
prevConn: nil,
|
||||
currentConn: pktConn,
|
||||
addrIndex: index,
|
||||
recvQueue: make(chan *udpPacket, packetQueueSize),
|
||||
closeChan: make(chan struct{}),
|
||||
bufPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, udpBufferSize)
|
||||
@@ -115,12 +121,13 @@ func (u *udpHopPacketConn) recvLoop(conn net.PacketConn) {
|
||||
}
|
||||
|
||||
func (u *udpHopPacketConn) hopLoop() {
|
||||
ticker := time.NewTicker(u.HopInterval)
|
||||
ticker := time.NewTicker(time.Duration(crypto.RandBetween(u.HopIntervalMin, u.HopIntervalMax)) * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
u.hop()
|
||||
ticker.Reset(time.Duration(crypto.RandBetween(u.HopIntervalMin, u.HopIntervalMax)) * time.Second)
|
||||
case <-u.closeChan:
|
||||
return
|
||||
}
|
||||
|
||||
@@ -132,6 +132,8 @@ func Test_ListenXHAndDial_TLS(t *testing.T) {
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
streamSettings := &internet.MemoryStreamConfig{
|
||||
ProtocolName: "splithttp",
|
||||
ProtocolSettings: &Config{
|
||||
@@ -139,8 +141,8 @@ func Test_ListenXHAndDial_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 := ListenXH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||
@@ -228,6 +230,8 @@ func Test_ListenXHAndDial_QUIC(t *testing.T) {
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
streamSettings := &internet.MemoryStreamConfig{
|
||||
ProtocolName: "splithttp",
|
||||
ProtocolSettings: &Config{
|
||||
@@ -235,9 +239,9 @@ func Test_ListenXHAndDial_QUIC(t *testing.T) {
|
||||
},
|
||||
SecurityType: "tls",
|
||||
SecuritySettings: &tls.Config{
|
||||
AllowInsecure: true,
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))},
|
||||
NextProtocol: []string{"h3"},
|
||||
Certificate: []*tls.Certificate{tls.ParseCertificate(ct)},
|
||||
PinnedPeerCertSha256: [][]byte{ctHash[:]},
|
||||
NextProtocol: []string{"h3"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -35,23 +35,23 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
}
|
||||
|
||||
isFromMitmVerify := false
|
||||
if r, ok := tlsConfig.Rand.(*tls.RandCarrier); ok && len(r.VerifyPeerCertInNames) > 0 {
|
||||
for i, name := range r.VerifyPeerCertInNames {
|
||||
if r, ok := tlsConfig.Rand.(*tls.RandCarrier); ok && len(r.VerifyPeerCertByName) > 0 {
|
||||
for i, name := range r.VerifyPeerCertByName {
|
||||
if tls.IsFromMitm(name) {
|
||||
isFromMitmVerify = true
|
||||
r.VerifyPeerCertInNames[0], r.VerifyPeerCertInNames[i] = r.VerifyPeerCertInNames[i], r.VerifyPeerCertInNames[0]
|
||||
r.VerifyPeerCertInNames = r.VerifyPeerCertInNames[1:]
|
||||
r.VerifyPeerCertByName[0], r.VerifyPeerCertByName[i] = r.VerifyPeerCertByName[i], r.VerifyPeerCertByName[0]
|
||||
r.VerifyPeerCertByName = r.VerifyPeerCertByName[1:]
|
||||
after := mitmServerName
|
||||
for {
|
||||
if len(after) > 0 {
|
||||
r.VerifyPeerCertInNames = append(r.VerifyPeerCertInNames, after)
|
||||
r.VerifyPeerCertByName = append(r.VerifyPeerCertByName, after)
|
||||
}
|
||||
_, after, _ = strings.Cut(after, ".")
|
||||
if !strings.Contains(after, ".") {
|
||||
break
|
||||
}
|
||||
}
|
||||
slices.Reverse(r.VerifyPeerCertInNames)
|
||||
slices.Reverse(r.VerifyPeerCertByName)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
||||
}
|
||||
|
||||
// directly return success if pinned cert is leaf
|
||||
// or replace RootCAs if pinned cert is CA (and can be used in VerifyPeerCertInNames)
|
||||
// or replace RootCAs if pinned cert is CA (and can be used in VerifyPeerCertByName)
|
||||
CAs := r.RootCAs
|
||||
var verifyResult verifyResult
|
||||
var verifiedCert *x509.Certificate
|
||||
@@ -302,7 +302,7 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
||||
verifyResult, verifiedCert = verifyChain(certs, r.PinnedPeerCertSha256)
|
||||
switch verifyResult {
|
||||
case certNotFound:
|
||||
return errors.New("peer cert is unrecognized (againsts pinnedPeerCertSha256)")
|
||||
return errors.New("peer cert is unrecognized (against pinnedPeerCertSha256)")
|
||||
case foundLeaf:
|
||||
return nil
|
||||
case foundCA:
|
||||
@@ -313,7 +313,7 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
||||
}
|
||||
}
|
||||
|
||||
if r.VerifyPeerCertInNames != nil { // RAW's Dial() may make it empty but not nil
|
||||
if r.VerifyPeerCertByName != nil { // RAW's Dial() may make it empty but not nil
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: CAs,
|
||||
CurrentTime: time.Now(),
|
||||
@@ -322,15 +322,15 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
||||
for _, cert := range certs[1:] {
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
for _, opts.DNSName = range r.VerifyPeerCertInNames {
|
||||
for _, opts.DNSName = range r.VerifyPeerCertByName {
|
||||
if _, err := certs[0].Verify(opts); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if verifyResult == foundCA {
|
||||
errors.New("peer cert is invalid (againsts pinned CA and verifyPeerCertInNames)")
|
||||
errors.New("peer cert is invalid (against pinned CA and verifyPeerCertByName)")
|
||||
}
|
||||
return errors.New("peer cert is invalid (againsts root CAs and verifyPeerCertInNames)")
|
||||
return errors.New("peer cert is invalid (against root CAs and verifyPeerCertByName)")
|
||||
}
|
||||
|
||||
if verifyResult == foundCA { // if found CA, we need to verify here
|
||||
@@ -346,17 +346,17 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
||||
if _, err := certs[0].Verify(opts); err == nil {
|
||||
return nil
|
||||
}
|
||||
return errors.New("peer cert is invalid (againsts pinned CA and serverName)")
|
||||
return errors.New("peer cert is invalid (against pinned CA and serverName)")
|
||||
}
|
||||
|
||||
return nil // len(r.PinnedPeerCertSha256)==nil && len(r.VerifyPeerCertInNames)==nil
|
||||
return nil // r.PinnedPeerCertSha256==nil && r.verifyPeerCertByName==nil
|
||||
}
|
||||
|
||||
type RandCarrier struct {
|
||||
Config *tls.Config
|
||||
RootCAs *x509.CertPool
|
||||
VerifyPeerCertInNames []string
|
||||
PinnedPeerCertSha256 [][]byte
|
||||
Config *tls.Config
|
||||
RootCAs *x509.CertPool
|
||||
VerifyPeerCertByName []string
|
||||
PinnedPeerCertSha256 [][]byte
|
||||
}
|
||||
|
||||
func (r *RandCarrier) Read(p []byte) (n int, err error) {
|
||||
@@ -374,31 +374,28 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||
return &tls.Config{
|
||||
ClientSessionCache: globalSessionCache,
|
||||
RootCAs: root,
|
||||
InsecureSkipVerify: false,
|
||||
NextProtos: nil,
|
||||
SessionTicketsDisabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
randCarrier := &RandCarrier{
|
||||
RootCAs: root,
|
||||
VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames),
|
||||
PinnedPeerCertSha256: c.PinnedPeerCertSha256,
|
||||
RootCAs: root,
|
||||
VerifyPeerCertByName: slices.Clone(c.VerifyPeerCertByName),
|
||||
PinnedPeerCertSha256: c.PinnedPeerCertSha256,
|
||||
}
|
||||
config := &tls.Config{
|
||||
Rand: randCarrier,
|
||||
ClientSessionCache: globalSessionCache,
|
||||
RootCAs: root,
|
||||
InsecureSkipVerify: c.AllowInsecure,
|
||||
NextProtos: slices.Clone(c.NextProtocol),
|
||||
SessionTicketsDisabled: !c.EnableSessionResumption,
|
||||
VerifyPeerCertificate: randCarrier.verifyPeerCert,
|
||||
}
|
||||
randCarrier.Config = config
|
||||
if len(c.VerifyPeerCertInNames) > 0 {
|
||||
if len(c.VerifyPeerCertByName) > 0 {
|
||||
config.InsecureSkipVerify = true
|
||||
} else {
|
||||
randCarrier.VerifyPeerCertInNames = nil
|
||||
randCarrier.VerifyPeerCertByName = nil
|
||||
}
|
||||
if len(c.PinnedPeerCertSha256) > 0 {
|
||||
config.InsecureSkipVerify = true
|
||||
|
||||
@@ -181,8 +181,6 @@ type Config struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Whether or not to allow self-signed certificates.
|
||||
AllowInsecure bool `protobuf:"varint,1,opt,name=allow_insecure,json=allowInsecure,proto3" json:"allow_insecure,omitempty"`
|
||||
// List of certificates to be served on server.
|
||||
Certificate []*Certificate `protobuf:"bytes,2,rep,name=certificate,proto3" json:"certificate,omitempty"`
|
||||
// Override server name.
|
||||
@@ -203,26 +201,15 @@ type Config struct {
|
||||
// TLS Client Hello fingerprint (uTLS).
|
||||
Fingerprint string `protobuf:"bytes,11,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"`
|
||||
RejectUnknownSni bool `protobuf:"varint,12,opt,name=reject_unknown_sni,json=rejectUnknownSni,proto3" json:"reject_unknown_sni,omitempty"`
|
||||
// @Document Some certificate chain sha256 hashes.
|
||||
// @Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted.
|
||||
// @Critical
|
||||
PinnedPeerCertificateChainSha256 [][]byte `protobuf:"bytes,13,rep,name=pinned_peer_certificate_chain_sha256,json=pinnedPeerCertificateChainSha256,proto3" json:"pinned_peer_certificate_chain_sha256,omitempty"`
|
||||
// @Document Some certificate public key sha256 hashes.
|
||||
// @Document After normal validation (required), if one of certs in verified chain matches one of these values, the connection will be eventually accepted.
|
||||
// @Critical
|
||||
PinnedPeerCertificatePublicKeySha256 [][]byte `protobuf:"bytes,14,rep,name=pinned_peer_certificate_public_key_sha256,json=pinnedPeerCertificatePublicKeySha256,proto3" json:"pinned_peer_certificate_public_key_sha256,omitempty"`
|
||||
MasterKeyLog string `protobuf:"bytes,15,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"`
|
||||
MasterKeyLog string `protobuf:"bytes,15,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"`
|
||||
// Lists of string as CurvePreferences values.
|
||||
CurvePreferences []string `protobuf:"bytes,16,rep,name=curve_preferences,json=curvePreferences,proto3" json:"curve_preferences,omitempty"`
|
||||
// @Document Replaces server_name to verify the peer cert.
|
||||
// @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
||||
// @Critical
|
||||
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
|
||||
EchServerKeys []byte `protobuf:"bytes,18,opt,name=ech_server_keys,json=echServerKeys,proto3" json:"ech_server_keys,omitempty"`
|
||||
EchConfigList string `protobuf:"bytes,19,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
|
||||
EchForceQuery string `protobuf:"bytes,20,opt,name=ech_force_query,json=echForceQuery,proto3" json:"ech_force_query,omitempty"`
|
||||
EchSocketSettings *internet.SocketConfig `protobuf:"bytes,21,opt,name=ech_socket_settings,json=echSocketSettings,proto3" json:"ech_socket_settings,omitempty"`
|
||||
PinnedPeerCertSha256 [][]byte `protobuf:"bytes,22,rep,name=pinned_peer_cert_sha256,json=pinnedPeerCertSha256,proto3" json:"pinned_peer_cert_sha256,omitempty"`
|
||||
CurvePreferences []string `protobuf:"bytes,16,rep,name=curve_preferences,json=curvePreferences,proto3" json:"curve_preferences,omitempty"`
|
||||
VerifyPeerCertByName []string `protobuf:"bytes,17,rep,name=verify_peer_cert_by_name,json=verifyPeerCertByName,proto3" json:"verify_peer_cert_by_name,omitempty"`
|
||||
EchServerKeys []byte `protobuf:"bytes,18,opt,name=ech_server_keys,json=echServerKeys,proto3" json:"ech_server_keys,omitempty"`
|
||||
EchConfigList string `protobuf:"bytes,19,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
|
||||
EchForceQuery string `protobuf:"bytes,20,opt,name=ech_force_query,json=echForceQuery,proto3" json:"ech_force_query,omitempty"`
|
||||
EchSocketSettings *internet.SocketConfig `protobuf:"bytes,21,opt,name=ech_socket_settings,json=echSocketSettings,proto3" json:"ech_socket_settings,omitempty"`
|
||||
PinnedPeerCertSha256 [][]byte `protobuf:"bytes,22,rep,name=pinned_peer_cert_sha256,json=pinnedPeerCertSha256,proto3" json:"pinned_peer_cert_sha256,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -255,13 +242,6 @@ func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_tls_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Config) GetAllowInsecure() bool {
|
||||
if x != nil {
|
||||
return x.AllowInsecure
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetCertificate() []*Certificate {
|
||||
if x != nil {
|
||||
return x.Certificate
|
||||
@@ -332,20 +312,6 @@ func (x *Config) GetRejectUnknownSni() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetPinnedPeerCertificateChainSha256() [][]byte {
|
||||
if x != nil {
|
||||
return x.PinnedPeerCertificateChainSha256
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetPinnedPeerCertificatePublicKeySha256() [][]byte {
|
||||
if x != nil {
|
||||
return x.PinnedPeerCertificatePublicKeySha256
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetMasterKeyLog() string {
|
||||
if x != nil {
|
||||
return x.MasterKeyLog
|
||||
@@ -360,9 +326,9 @@ func (x *Config) GetCurvePreferences() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetVerifyPeerCertInNames() []string {
|
||||
func (x *Config) GetVerifyPeerCertByName() []string {
|
||||
if x != nil {
|
||||
return x.VerifyPeerCertInNames
|
||||
return x.VerifyPeerCertByName
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -435,81 +401,68 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
0x45, 0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14,
|
||||
0x0a, 0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49,
|
||||
0x46, 0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54,
|
||||
0x59, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xa0, 0x08, 0x0a, 0x06, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e,
|
||||
0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c,
|
||||
0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63,
|
||||
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 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, 0x74, 0x6c, 0x73, 0x2e, 0x43,
|
||||
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74,
|
||||
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||
0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65,
|
||||
0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74,
|
||||
0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||
0x0c, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a,
|
||||
0x19, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
|
||||
0x72, 0x65, 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x17, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52,
|
||||
0x65, 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74,
|
||||
0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53,
|
||||
0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e,
|
||||
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
|
||||
0x6d, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61,
|
||||
0x78, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0a, 0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63,
|
||||
0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73,
|
||||
0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18,
|
||||
0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69,
|
||||
0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e, 0x6b,
|
||||
0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
|
||||
0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e, 0x69,
|
||||
0x12, 0x4e, 0x0a, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f,
|
||||
0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x69,
|
||||
0x6e, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x20,
|
||||
0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
|
||||
0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36,
|
||||
0x12, 0x57, 0x0a, 0x29, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f,
|
||||
0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c,
|
||||
0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0e, 0x20,
|
||||
0x03, 0x28, 0x0c, 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43,
|
||||
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73,
|
||||
0x59, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xce, 0x06, 0x0a, 0x06, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x65, 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, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
|
||||
0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d,
|
||||
0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||
0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x19, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x70, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x65, 0x6e, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x79,
|
||||
0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x6f,
|
||||
0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x56, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73,
|
||||
0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x69, 0x70,
|
||||
0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e,
|
||||
0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
|
||||
0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72,
|
||||
0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e,
|
||||
0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x55,
|
||||
0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e, 0x69, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73,
|
||||
0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12,
|
||||
0x2b, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
|
||||
0x6e, 0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x76,
|
||||
0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x19,
|
||||
0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x18,
|
||||
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74,
|
||||
0x5f, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||
0x15, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49,
|
||||
0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x73, 0x65,
|
||||
0x72, 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x0d, 0x65, 0x63, 0x68, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x26,
|
||||
0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6c, 0x69, 0x73,
|
||||
0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x66, 0x6f,
|
||||
0x72, 0x63, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0d, 0x65, 0x63, 0x68, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x55,
|
||||
0x0a, 0x13, 0x65, 0x63, 0x68, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x74,
|
||||
0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x15, 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, 0x11, 0x65, 0x63, 0x68, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74,
|
||||
0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f,
|
||||
0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36,
|
||||
0x18, 0x16, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65,
|
||||
0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x42, 0x73, 0x0a, 0x1f,
|
||||
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, 0x2e, 0x74, 0x6c, 0x73, 0x50,
|
||||
0x01, 0x5a, 0x30, 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, 0x2f,
|
||||
0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c,
|
||||
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x5f, 0x62, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14,
|
||||
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x42, 0x79,
|
||||
0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x73, 0x65, 0x72, 0x76,
|
||||
0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65,
|
||||
0x63, 0x68, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x26, 0x0a, 0x0f,
|
||||
0x65, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18,
|
||||
0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x66, 0x6f, 0x72, 0x63,
|
||||
0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65,
|
||||
0x63, 0x68, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x55, 0x0a, 0x13,
|
||||
0x65, 0x63, 0x68, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x18, 0x15, 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, 0x11, 0x65, 0x63, 0x68, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65,
|
||||
0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x16,
|
||||
0x20, 0x03, 0x28, 0x0c, 0x52, 0x14, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72,
|
||||
0x43, 0x65, 0x72, 0x74, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x42, 0x73, 0x0a, 0x1f, 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, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a,
|
||||
0x30, 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, 0x2f, 0x74, 0x6c,
|
||||
0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -38,9 +38,6 @@ message Certificate {
|
||||
}
|
||||
|
||||
message Config {
|
||||
// Whether or not to allow self-signed certificates.
|
||||
bool allow_insecure = 1;
|
||||
|
||||
// List of certificates to be served on server.
|
||||
repeated Certificate certificate = 2;
|
||||
|
||||
@@ -70,29 +67,13 @@ message Config {
|
||||
string fingerprint = 11;
|
||||
|
||||
bool reject_unknown_sni = 12;
|
||||
|
||||
/* @Document Some certificate chain sha256 hashes.
|
||||
@Document After normal validation or allow_insecure, if the server's cert chain hash does not match any of these values, the connection will be aborted.
|
||||
@Critical
|
||||
*/
|
||||
repeated bytes pinned_peer_certificate_chain_sha256 = 13;
|
||||
|
||||
/* @Document Some certificate public key sha256 hashes.
|
||||
@Document After normal validation (required), if one of certs in verified chain matches one of these values, the connection will be eventually accepted.
|
||||
@Critical
|
||||
*/
|
||||
repeated bytes pinned_peer_certificate_public_key_sha256 = 14;
|
||||
|
||||
string master_key_log = 15;
|
||||
|
||||
// Lists of string as CurvePreferences values.
|
||||
repeated string curve_preferences = 16;
|
||||
|
||||
/* @Document Replaces server_name to verify the peer cert.
|
||||
@Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
||||
@Critical
|
||||
*/
|
||||
repeated string verify_peer_cert_in_names = 17;
|
||||
repeated string verify_peer_cert_by_name = 17;
|
||||
|
||||
bytes ech_server_keys = 18;
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
)
|
||||
|
||||
func TestCertificateIssuing(t *testing.T) {
|
||||
certificate := ParseCertificate(cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign)))
|
||||
ct, _ := cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign))
|
||||
certificate := ParseCertificate(ct)
|
||||
certificate.Usage = Certificate_AUTHORITY_ISSUE
|
||||
|
||||
c := &Config{
|
||||
@@ -35,8 +36,8 @@ func TestCertificateIssuing(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExpiredCertificate(t *testing.T) {
|
||||
caCert := cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign))
|
||||
expiredCert := cert.MustGenerate(caCert, cert.NotAfter(time.Now().Add(time.Minute*-2)), cert.CommonName("www.example.com"), cert.DNSNames("www.example.com"))
|
||||
caCert, _ := cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign))
|
||||
expiredCert, _ := cert.MustGenerate(caCert, cert.NotAfter(time.Now().Add(time.Minute*-2)), cert.CommonName("www.example.com"), cert.DNSNames("www.example.com"))
|
||||
|
||||
certificate := ParseCertificate(caCert)
|
||||
certificate.Usage = Certificate_AUTHORITY_ISSUE
|
||||
@@ -73,7 +74,8 @@ func TestInsecureCertificates(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkCertificateIssuing(b *testing.B) {
|
||||
certificate := ParseCertificate(cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign)))
|
||||
ct, _ := cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign))
|
||||
certificate := ParseCertificate(ct)
|
||||
certificate.Usage = Certificate_AUTHORITY_ISSUE
|
||||
|
||||
c := &Config{
|
||||
|
||||
@@ -100,16 +100,14 @@ uI6HqHFD3iEct8fBkYfQiwH2e1eu9OwgujiWHsutyK8VvzVB3/YnhQ/TzciRjPqz
|
||||
}
|
||||
|
||||
func TestVerifyPeerLeafCert(t *testing.T) {
|
||||
leafCert := cert.MustGenerate(nil, cert.DNSNames("example.com"))
|
||||
leafCert, leafHash := cert.MustGenerate(nil, cert.DNSNames("example.com"))
|
||||
leaf := common.Must2(x509.ParseCertificate(leafCert.Certificate))
|
||||
|
||||
caHash := GenerateCertHash(leafCert.Certificate)
|
||||
|
||||
r := &RandCarrier{
|
||||
Config: &tls.Config{
|
||||
ServerName: "example.com",
|
||||
},
|
||||
PinnedPeerCertSha256: [][]byte{caHash},
|
||||
PinnedPeerCertSha256: [][]byte{leafHash[:]},
|
||||
}
|
||||
|
||||
rawCerts := [][]byte{leaf.Raw}
|
||||
@@ -127,19 +125,17 @@ func TestVerifyPeerLeafCert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVerifyPeerCACert(t *testing.T) {
|
||||
caCert := cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign))
|
||||
caCert, caHash := cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign))
|
||||
ca := common.Must2(x509.ParseCertificate(caCert.Certificate))
|
||||
|
||||
leafCert := cert.MustGenerate(caCert, cert.DNSNames("example.com"))
|
||||
leafCert, _ := cert.MustGenerate(caCert, cert.DNSNames("example.com"))
|
||||
leaf := common.Must2(x509.ParseCertificate(leafCert.Certificate))
|
||||
|
||||
caHash := GenerateCertHash(ca)
|
||||
|
||||
r := &RandCarrier{
|
||||
Config: &tls.Config{
|
||||
ServerName: "example.com",
|
||||
},
|
||||
PinnedPeerCertSha256: [][]byte{caHash},
|
||||
PinnedPeerCertSha256: [][]byte{caHash[:]},
|
||||
}
|
||||
|
||||
rawCerts := [][]byte{leaf.Raw, ca.Raw}
|
||||
|
||||
@@ -123,6 +123,8 @@ func Test_listenWSAndDial_TLS(t *testing.T) {
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ct, ctHash := cert.MustGenerate(nil, cert.CommonName("localhost"))
|
||||
|
||||
streamSettings := &internet.MemoryStreamConfig{
|
||||
ProtocolName: "websocket",
|
||||
ProtocolSettings: &Config{
|
||||
@@ -130,8 +132,8 @@ func Test_listenWSAndDial_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 := ListenWS(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) {
|
||||
|
||||
Reference in New Issue
Block a user