Compare commits

..
Author SHA1 Message Date
patterniha ea508727de add buffer 2025-08-31 01:37:12 +03:30
37 changed files with 438 additions and 791 deletions
-42
View File
@@ -196,47 +196,6 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
return inboundLink, outboundLink return inboundLink, outboundLink
} }
func (d *DefaultDispatcher) WrapLink(ctx context.Context, link *transport.Link) *transport.Link {
sessionInbound := session.InboundFromContext(ctx)
var user *protocol.MemoryUser
if sessionInbound != nil {
user = sessionInbound.User
}
link.Reader = &buf.TimeoutWrapperReader{Reader: link.Reader}
if user != nil && len(user.Email) > 0 {
p := d.policy.ForLevel(user.Level)
if p.Stats.UserUplink {
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
link.Reader.(*buf.TimeoutWrapperReader).Counter = c
}
}
if p.Stats.UserDownlink {
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
link.Writer = &SizeStatWriter{
Counter: c,
Writer: link.Writer,
}
}
}
if p.Stats.UserOnline {
name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
sessionInbounds := session.InboundFromContext(ctx)
userIP := sessionInbounds.Source.Address.String()
om.AddIP(userIP)
// log Online user with ips
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
}
}
}
return link
}
func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool { func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
domain := result.Domain() domain := result.Domain()
if domain == "" { if domain == "" {
@@ -357,7 +316,6 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
content = new(session.Content) content = new(session.Content)
ctx = session.ContextWithContent(ctx, content) ctx = session.ContextWithContent(ctx, content)
} }
outbound = d.WrapLink(ctx, outbound)
sniffingRequest := content.SniffingRequest sniffingRequest := content.SniffingRequest
if !sniffingRequest.Enabled { if !sniffingRequest.Enabled {
d.routedDispatch(ctx, outbound, destination) d.routedDispatch(ctx, outbound, destination)
+3 -3
View File
@@ -4,7 +4,6 @@ import (
"context" "context"
"time" "time"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux" "github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
@@ -149,6 +148,7 @@ func (w *BridgeWorker) Connections() uint32 {
} }
func (w *BridgeWorker) handleInternalConn(link *transport.Link) { func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
go func() {
reader := link.Reader reader := link.Reader
for { for {
mb, err := reader.ReadMultiBuffer() mb, err := reader.ReadMultiBuffer()
@@ -166,6 +166,7 @@ func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
} }
} }
} }
}()
} }
func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) { func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
@@ -180,7 +181,7 @@ func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*tra
uplinkReader, uplinkWriter := pipe.New(opt...) uplinkReader, uplinkWriter := pipe.New(opt...)
downlinkReader, downlinkWriter := pipe.New(opt...) downlinkReader, downlinkWriter := pipe.New(opt...)
go w.handleInternalConn(&transport.Link{ w.handleInternalConn(&transport.Link{
Reader: downlinkReader, Reader: downlinkReader,
Writer: uplinkWriter, Writer: uplinkWriter,
}) })
@@ -199,7 +200,6 @@ func (w *BridgeWorker) DispatchLink(ctx context.Context, dest net.Destination, l
return w.dispatcher.DispatchLink(ctx, dest, link) return w.dispatcher.DispatchLink(ctx, dest, link)
} }
link = w.dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
w.handleInternalConn(link) w.handleInternalConn(link)
return nil return nil
+4 -17
View File
@@ -30,7 +30,6 @@ type TimeoutReader interface {
type TimeoutWrapperReader struct { type TimeoutWrapperReader struct {
Reader Reader
stats.Counter
mb MultiBuffer mb MultiBuffer
err error err error
done chan struct{} done chan struct{}
@@ -40,16 +39,11 @@ func (r *TimeoutWrapperReader) ReadMultiBuffer() (MultiBuffer, error) {
if r.done != nil { if r.done != nil {
<-r.done <-r.done
r.done = nil r.done = nil
if r.Counter != nil {
r.Counter.Add(int64(r.mb.Len()))
}
return r.mb, r.err return r.mb, r.err
} }
r.mb, r.err = r.Reader.ReadMultiBuffer() r.mb = nil
if r.Counter != nil { r.err = nil
r.Counter.Add(int64(r.mb.Len())) return r.Reader.ReadMultiBuffer()
}
return r.mb, r.err
} }
func (r *TimeoutWrapperReader) ReadMultiBufferTimeout(duration time.Duration) (MultiBuffer, error) { func (r *TimeoutWrapperReader) ReadMultiBufferTimeout(duration time.Duration) (MultiBuffer, error) {
@@ -60,19 +54,12 @@ func (r *TimeoutWrapperReader) ReadMultiBufferTimeout(duration time.Duration) (M
close(r.done) close(r.done)
}() }()
} }
timeout := make(chan struct{})
go func() {
time.Sleep(duration) time.Sleep(duration)
close(timeout)
}()
select { select {
case <-r.done: case <-r.done:
r.done = nil r.done = nil
if r.Counter != nil {
r.Counter.Add(int64(r.mb.Len()))
}
return r.mb, r.err return r.mb, r.err
case <-timeout: default:
return nil, nil return nil, nil
} }
} }
-14
View File
@@ -78,7 +78,6 @@ type BufferedWriter struct {
writer Writer writer Writer
buffer *Buffer buffer *Buffer
buffered bool buffered bool
flushNext bool
} }
// NewBufferedWriter creates a new BufferedWriter. // NewBufferedWriter creates a new BufferedWriter.
@@ -162,12 +161,6 @@ func (w *BufferedWriter) WriteMultiBuffer(b MultiBuffer) error {
} }
} }
if w.flushNext {
w.buffered = false
w.flushNext = false
return w.flushInternal()
}
return nil return nil
} }
@@ -208,13 +201,6 @@ func (w *BufferedWriter) SetBuffered(f bool) error {
return nil return nil
} }
// SetFlushNext will wait the next WriteMultiBuffer to flush and set buffered = false
func (w *BufferedWriter) SetFlushNext() {
w.Lock()
defer w.Unlock()
w.flushNext = true
}
// ReadFrom implements io.ReaderFrom. // ReadFrom implements io.ReaderFrom.
func (w *BufferedWriter) ReadFrom(reader io.Reader) (int64, error) { func (w *BufferedWriter) ReadFrom(reader io.Reader) (int64, error) {
if err := w.SetBuffered(false); err != nil { if err := w.SetBuffered(false); err != nil {
-3
View File
@@ -10,9 +10,6 @@ func RandBetween(from int64, to int64) int64 {
if from == to { if from == to {
return from return from
} }
if from > to {
from, to = to, from
}
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from)) bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
return from + bigInt.Int64() return from + bigInt.Int64()
} }
-2
View File
@@ -4,7 +4,6 @@ import (
"context" "context"
"io" "io"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
@@ -62,7 +61,6 @@ func (s *Server) DispatchLink(ctx context.Context, dest net.Destination, link *t
if dest.Address != muxCoolAddress { if dest.Address != muxCoolAddress {
return s.dispatcher.DispatchLink(ctx, dest, link) return s.dispatcher.DispatchLink(ctx, dest, link)
} }
link = s.dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
_, err := NewServerWorker(ctx, s.dispatcher, link) _, err := NewServerWorker(ctx, s.dispatcher, link)
return err return err
} }
-34
View File
@@ -1,34 +0,0 @@
package http
import (
"context"
gohttp "net/http"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/transport/internet"
)
// NewClient creates an HTTP client with with internal dialer and using the given sockopt.
// sockopt can only have one or empty.
func NewClient(sockopt ...*internet.SocketConfig) *gohttp.Client {
var Sockopt *internet.SocketConfig
switch len(sockopt) {
case 0:
case 1:
Sockopt = sockopt[0]
default:
panic("sockopt can only be nil or have one")
}
httpClient := &gohttp.Client{
Transport: &gohttp.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dest, err := net.ParseDestination(network + ":" + addr)
if err != nil {
return nil, err
}
return internet.DialSystem(ctx, dest, Sockopt)
},
},
}
return httpClient
}
+2 -2
View File
@@ -18,8 +18,8 @@ import (
var ( var (
Version_x byte = 25 Version_x byte = 25
Version_y byte = 9 Version_y byte = 8
Version_z byte = 5 Version_z byte = 29
) )
var ( var (
+12 -36
View File
@@ -74,7 +74,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
} }
if account.Encryption != "" { if account.Encryption != "" {
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`) return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
} }
user.Account = serial.ToTypedMessage(account) user.Account = serial.ToTypedMessage(account)
@@ -96,34 +96,23 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
default: default:
return false return false
} }
t := strings.SplitN(strings.TrimSuffix(s[2], "s"), "-", 2) if s[2] != "1rtt" {
i, err := strconv.Atoi(t[0]) t := strings.TrimSuffix(s[2], "s")
if t == s[2] {
return false
}
i, err := strconv.Atoi(t)
if err != nil { if err != nil {
return false return false
} }
config.SecondsFrom = int64(i) config.Seconds = uint32(i)
if len(t) == 2 {
i, err := strconv.Atoi(t[1])
if err != nil {
return false
} }
config.SecondsTo = int64(i) for i := 3; i < len(s); i++ {
} if b, _ := base64.RawURLEncoding.DecodeString(s[i]); len(b) != 32 && len(b) != 64 {
padding := 0
for _, r := range s[3:] {
if len(r) < 20 {
padding += len(r) + 1
continue
}
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 64 {
return false return false
} }
} }
config.Decryption = config.Decryption[27+len(s[2]):] config.Decryption = config.Decryption[27+len(s[2]):]
if padding > 0 {
config.Padding = config.Decryption[:padding-1]
config.Decryption = config.Decryption[padding:]
}
return true return true
}() && config.Decryption != "none" { }() && config.Decryption != "none" {
if config.Decryption == "" { if config.Decryption == "" {
@@ -132,10 +121,6 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
return nil, errors.New(`VLESS settings: unsupported "decryption": ` + config.Decryption) return nil, errors.New(`VLESS settings: unsupported "decryption": ` + config.Decryption)
} }
if config.Decryption != "none" && c.Fallbacks != nil {
return nil, errors.New(`VLESS settings: "fallbacks" can not be used together with "decryption"`)
}
for _, fb := range c.Fallbacks { for _, fb := range c.Fallbacks {
var i uint16 var i uint16
var s string var s string
@@ -265,21 +250,12 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
default: default:
return false return false
} }
padding := 0 for i := 3; i < len(s); i++ {
for _, r := range s[3:] { if b, _ := base64.RawURLEncoding.DecodeString(s[i]); len(b) != 32 && len(b) != 1184 {
if len(r) < 20 {
padding += len(r) + 1
continue
}
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 1184 {
return false return false
} }
} }
account.Encryption = account.Encryption[27+len(s[2]):] account.Encryption = account.Encryption[27+len(s[2]):]
if padding > 0 {
account.Padding = account.Encryption[:padding-1]
account.Encryption = account.Encryption[padding:]
}
return true return true
}() && account.Encryption != "none" { }() && account.Encryption != "none" {
if account.Encryption == "" { if account.Encryption == "" {
-1
View File
@@ -18,6 +18,5 @@ func init() {
cmdWG, cmdWG,
cmdMLDSA65, cmdMLDSA65,
cmdMLKEM768, cmdMLKEM768,
cmdVLESSEnc,
) )
} }
+7 -19
View File
@@ -25,21 +25,6 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
return return
} }
} }
privateKey, password, hash32, err := genCurve25519(privateKey)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("PrivateKey: %v\nPassword: %v\nHash32: %v\n",
encoding.EncodeToString(privateKey),
encoding.EncodeToString(password),
encoding.EncodeToString(hash32[:]))
}
func genCurve25519(inputPrivateKey []byte) (privateKey []byte, password []byte, hash32 [32]byte, returnErr error) {
if len(inputPrivateKey) > 0 {
privateKey = inputPrivateKey
}
if privateKey == nil { if privateKey == nil {
privateKey = make([]byte, 32) privateKey = make([]byte, 32)
rand.Read(privateKey) rand.Read(privateKey)
@@ -54,10 +39,13 @@ func genCurve25519(inputPrivateKey []byte) (privateKey []byte, password []byte,
key, err := ecdh.X25519().NewPrivateKey(privateKey) key, err := ecdh.X25519().NewPrivateKey(privateKey)
if err != nil { if err != nil {
returnErr = err fmt.Println(err.Error())
return return
} }
password = key.PublicKey().Bytes() password := key.PublicKey().Bytes()
hash32 = blake3.Sum256(password) hash32 := blake3.Sum256(password)
return fmt.Printf("PrivateKey: %v\nPassword: %v\nHash32: %v",
encoding.EncodeToString(privateKey),
encoding.EncodeToString(password),
encoding.EncodeToString(hash32[:]))
} }
+1 -1
View File
@@ -40,7 +40,7 @@ func executeMLDSA65(cmd *base.Command, args []string) {
rand.Read(seed[:]) rand.Read(seed[:])
} }
pub, _ := mldsa65.NewKeyFromSeed(&seed) pub, _ := mldsa65.NewKeyFromSeed(&seed)
fmt.Printf("Seed: %v\nVerify: %v\n", fmt.Printf("Seed: %v\nVerify: %v",
base64.RawURLEncoding.EncodeToString(seed[:]), base64.RawURLEncoding.EncodeToString(seed[:]),
base64.RawURLEncoding.EncodeToString(pub.Bytes())) base64.RawURLEncoding.EncodeToString(pub.Bytes()))
} }
+6 -16
View File
@@ -12,9 +12,9 @@ import (
var cmdMLKEM768 = &base.Command{ var cmdMLKEM768 = &base.Command{
UsageLine: `{{.Exec}} mlkem768 [-i "seed (base64.RawURLEncoding)"]`, UsageLine: `{{.Exec}} mlkem768 [-i "seed (base64.RawURLEncoding)"]`,
Short: `Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS Encryption)`, Short: `Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS)`,
Long: ` Long: `
Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS Encryption). Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS).
Random: {{.Exec}} mlkem768 Random: {{.Exec}} mlkem768
@@ -40,21 +40,11 @@ func executeMLKEM768(cmd *base.Command, args []string) {
} else { } else {
rand.Read(seed[:]) rand.Read(seed[:])
} }
seed, client, hash32 := genMLKEM768(&seed) key, _ := mlkem.NewDecapsulationKey768(seed[:])
fmt.Printf("Seed: %v\nClient: %v\nHash32: %v\n", client := key.EncapsulationKey().Bytes()
hash32 := blake3.Sum256(client)
fmt.Printf("Seed: %v\nClient: %v\nHash32: %v",
base64.RawURLEncoding.EncodeToString(seed[:]), base64.RawURLEncoding.EncodeToString(seed[:]),
base64.RawURLEncoding.EncodeToString(client), base64.RawURLEncoding.EncodeToString(client),
base64.RawURLEncoding.EncodeToString(hash32[:])) base64.RawURLEncoding.EncodeToString(hash32[:]))
} }
func genMLKEM768(inputSeed *[64]byte) (seed [64]byte, client []byte, hash32 [32]byte) {
if inputSeed == nil {
rand.Read(seed[:])
} else {
seed = *inputSeed
}
key, _ := mlkem.NewDecapsulationKey768(seed[:])
client = key.EncapsulationKey().Bytes()
hash32 = blake3.Sum256(client)
return
}
-41
View File
@@ -1,41 +0,0 @@
package all
import (
"encoding/base64"
"fmt"
"strings"
"github.com/xtls/xray-core/main/commands/base"
)
var cmdVLESSEnc = &base.Command{
UsageLine: `{{.Exec}} vlessenc`,
Short: `Generate decryption/encryption json pair (VLESS Encryption)`,
Long: `
Generate decryption/encryption json pair (VLESS Encryption).
`,
}
func init() {
cmdVLESSEnc.Run = executeVLESSEnc // break init loop
}
func executeVLESSEnc(cmd *base.Command, args []string) {
privateKey, password, _, _ := genCurve25519(nil)
serverKey := base64.RawURLEncoding.EncodeToString(privateKey)
clientKey := base64.RawURLEncoding.EncodeToString(password)
decryption := generateDotConfig("mlkem768x25519plus", "native", "600s", serverKey)
encryption := generateDotConfig("mlkem768x25519plus", "native", "0rtt", clientKey)
seed, client, _ := genMLKEM768(nil)
serverKeyPQ := base64.RawURLEncoding.EncodeToString(seed[:])
clientKeyPQ := base64.RawURLEncoding.EncodeToString(client)
decryptionPQ := generateDotConfig("mlkem768x25519plus", "native", "600s", serverKeyPQ)
encryptionPQ := generateDotConfig("mlkem768x25519plus", "native", "0rtt", clientKeyPQ)
fmt.Printf("Choose one Authentication to use, do not mix them. Ephemeral key exchange is Post-Quantum safe anyway.\n\n")
fmt.Printf("Authentication: X25519, not Post-Quantum\n\"decryption\": \"%v\"\n\"encryption\": \"%v\"\n\n", decryption, encryption)
fmt.Printf("Authentication: ML-KEM-768, Post-Quantum\n\"decryption\": \"%v\"\n\"encryption\": \"%v\"\n", decryptionPQ, encryptionPQ)
}
func generateDotConfig(fields ...string) string {
return strings.Join(fields, ".")
}
+2 -2
View File
@@ -6,9 +6,9 @@ import (
var cmdX25519 = &base.Command{ var cmdX25519 = &base.Command{
UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"] [--std-encoding]`, UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"] [--std-encoding]`,
Short: `Generate key pair for X25519 key exchange (REALITY, VLESS Encryption)`, Short: `Generate key pair for X25519 key exchange (VLESS, REALITY)`,
Long: ` Long: `
Generate key pair for X25519 key exchange (REALITY, VLESS Encryption). Generate key pair for X25519 key exchange (VLESS, REALITY).
Random: {{.Exec}} x25519 Random: {{.Exec}} x25519
+20 -50
View File
@@ -4,7 +4,6 @@ import (
"context" "context"
go_errors "errors" go_errors "errors"
"io" "io"
"strings"
"sync" "sync"
"time" "time"
@@ -169,15 +168,11 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
} }
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
terminate := func() { timer := signal.CancelAfterInactivity(ctx, cancel, h.timeout)
cancel()
conn.Close()
}
timer := signal.CancelAfterInactivity(ctx, terminate, h.timeout)
defer timer.SetTimeout(0)
request := func() error { request := func() error {
defer timer.SetTimeout(0) defer conn.Close()
for { for {
b, err := reader.ReadMessage() b, err := reader.ReadMessage()
if err == io.EOF { if err == io.EOF {
@@ -195,33 +190,24 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
if len(h.blockTypes) > 0 { if len(h.blockTypes) > 0 {
for _, blocktype := range h.blockTypes { for _, blocktype := range h.blockTypes {
if blocktype == int32(qType) { if blocktype == int32(qType) {
b.Release()
errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain)
if h.nonIPQuery == "reject" { if h.nonIPQuery == "reject" {
err := h.rejectNonIPQuery(id, qType, domain, writer) go h.rejectNonIPQuery(id, qType, domain, writer)
if err != nil {
return err
}
} }
errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain)
return nil return nil
} }
} }
} }
if isIPQuery { if isIPQuery {
b.Release() go h.handleIPQuery(id, qType, domain, writer)
go h.handleIPQuery(id, qType, domain, writer, timer)
continue
} }
if h.nonIPQuery == "drop" { if isIPQuery || h.nonIPQuery == "drop" {
b.Release() b.Release()
continue continue
} }
if h.nonIPQuery == "reject" { if h.nonIPQuery == "reject" {
go h.rejectNonIPQuery(id, qType, domain, writer)
b.Release() b.Release()
err := h.rejectNonIPQuery(id, qType, domain, writer)
if err != nil {
return err
}
continue continue
} }
} }
@@ -233,7 +219,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
} }
response := func() error { response := func() error {
defer timer.SetTimeout(0)
for { for {
b, err := connReader.ReadMessage() b, err := connReader.ReadMessage()
if err == io.EOF { if err == io.EOF {
@@ -259,7 +244,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
return nil return nil
} }
func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, timer *signal.ActivityTimer) { func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) {
var ips []net.IP var ips []net.IP
var err error var err error
@@ -334,21 +319,16 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
if err != nil { if err != nil {
errors.LogInfoInner(context.Background(), err, "pack message") errors.LogInfoInner(context.Background(), err, "pack message")
b.Release() b.Release()
timer.SetTimeout(0) return
} }
b.Resize(0, int32(len(msgBytes))) b.Resize(0, int32(len(msgBytes)))
if err := writer.WriteMessage(b); err != nil { if err := writer.WriteMessage(b); err != nil {
errors.LogInfoInner(context.Background(), err, "write IP answer") errors.LogInfoInner(context.Background(), err, "write IP answer")
timer.SetTimeout(0)
} }
} }
func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) error { func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) {
domainT := strings.TrimSuffix(domain, ".")
if domainT == "" {
return errors.New("empty domain name")
}
b := buf.New() b := buf.New()
rawBytes := b.Extend(buf.Size) rawBytes := b.Extend(buf.Size)
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{ builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
@@ -369,22 +349,20 @@ func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain stri
if err != nil { if err != nil {
errors.LogInfo(context.Background(), "unexpected domain ", domain, " when building reject message: ", err) errors.LogInfo(context.Background(), "unexpected domain ", domain, " when building reject message: ", err)
b.Release() b.Release()
return err return
} }
msgBytes, err := builder.Finish() msgBytes, err := builder.Finish()
if err != nil { if err != nil {
errors.LogInfoInner(context.Background(), err, "pack reject message") errors.LogInfoInner(context.Background(), err, "pack reject message")
b.Release() b.Release()
return err return
} }
b.Resize(0, int32(len(msgBytes))) b.Resize(0, int32(len(msgBytes)))
if err := writer.WriteMessage(b); err != nil { if err := writer.WriteMessage(b); err != nil {
errors.LogInfoInner(context.Background(), err, "write reject answer") errors.LogInfoInner(context.Background(), err, "write reject answer")
return err
} }
return nil
} }
type outboundConn struct { type outboundConn struct {
@@ -393,7 +371,6 @@ type outboundConn struct {
conn net.Conn conn net.Conn
connReady chan struct{} connReady chan struct{}
closed bool
} }
func (c *outboundConn) dial() error { func (c *outboundConn) dial() error {
@@ -408,16 +385,12 @@ func (c *outboundConn) dial() error {
func (c *outboundConn) Write(b []byte) (int, error) { func (c *outboundConn) Write(b []byte) (int, error) {
c.access.Lock() c.access.Lock()
if c.closed {
c.access.Unlock()
return 0, errors.New("outbound connection closed")
}
if c.conn == nil { if c.conn == nil {
if err := c.dial(); err != nil { if err := c.dial(); err != nil {
c.access.Unlock() c.access.Unlock()
errors.LogWarningInner(context.Background(), err, "failed to dial outbound connection") errors.LogWarningInner(context.Background(), err, "failed to dial outbound connection")
return 0, err return len(b), nil
} }
} }
@@ -427,27 +400,24 @@ func (c *outboundConn) Write(b []byte) (int, error) {
} }
func (c *outboundConn) Read(b []byte) (int, error) { func (c *outboundConn) Read(b []byte) (int, error) {
var conn net.Conn
c.access.Lock() c.access.Lock()
if c.closed { conn = c.conn
c.access.Unlock() c.access.Unlock()
return 0, io.EOF
}
if c.conn == nil { if conn == nil {
c.access.Unlock()
_, open := <-c.connReady _, open := <-c.connReady
if !open { if !open {
return 0, io.EOF return 0, io.EOF
} }
return c.conn.Read(b) conn = c.conn
} }
c.access.Unlock()
return c.conn.Read(b) return conn.Read(b)
} }
func (c *outboundConn) Close() error { func (c *outboundConn) Close() error {
c.access.Lock() c.access.Lock()
c.closed = true
close(c.connReady) close(c.connReady)
if c.conn != nil { if c.conn != nil {
c.conn.Close() c.conn.Close()
+1 -1
View File
@@ -182,7 +182,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
} }
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{ if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
Reader: reader, Reader: &buf.TimeoutWrapperReader{Reader: reader},
Writer: writer}, Writer: writer},
); err != nil { ); err != nil {
return errors.New("failed to dispatch request").Base(err) return errors.New("failed to dispatch request").Base(err)
+21 -2
View File
@@ -26,6 +26,7 @@ import (
"github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls"
) )
var useSplice bool var useSplice bool
@@ -211,15 +212,17 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
responseDone := func() error { responseDone := func() error {
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
if destination.Network == net.Network_TCP && useSplice && proxy.IsRAWTransportWithoutSecurity(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic if destination.Network == net.Network_TCP {
var writeConn net.Conn var writeConn net.Conn
var inTimer *signal.ActivityTimer var inTimer *signal.ActivityTimer
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil { if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil && useSplice {
writeConn = inbound.Conn writeConn = inbound.Conn
inTimer = inbound.Timer inTimer = inbound.Timer
} }
if !isTLSConn(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer) return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer)
} }
}
var reader buf.Reader var reader buf.Reader
if destination.Network == net.Network_TCP { if destination.Network == net.Network_TCP {
reader = buf.NewReader(conn) reader = buf.NewReader(conn)
@@ -243,6 +246,22 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
return nil return nil
} }
func isTLSConn(conn stat.Connection) bool {
if conn != nil {
statConn, ok := conn.(*stat.CounterConnection)
if ok {
conn = statConn.Connection
}
if _, ok := conn.(*tls.Conn); ok {
return true
}
if _, ok := conn.(*tls.UConn); ok {
return true
}
}
return false
}
func NewPacketReader(conn net.Conn, UDPOverride net.Destination, DialDest net.Destination) buf.Reader { func NewPacketReader(conn net.Conn, UDPOverride net.Destination, DialDest net.Destination) buf.Reader {
iConn := conn iConn := conn
statConn, ok := iConn.(*stat.CounterConnection) statConn, ok := iConn.(*stat.CounterConnection)
+2 -2
View File
@@ -96,7 +96,7 @@ func (s *Server) ProcessWithFirstbyte(ctx context.Context, network net.Network,
inbound.User = &protocol.MemoryUser{ inbound.User = &protocol.MemoryUser{
Level: s.config.UserLevel, Level: s.config.UserLevel,
} }
if !proxy.IsRAWTransportWithoutSecurity(conn) { if !proxy.IsRAWTransport(conn) {
inbound.CanSpliceCopy = 3 inbound.CanSpliceCopy = 3
} }
var reader *bufio.Reader var reader *bufio.Reader
@@ -193,7 +193,7 @@ func (s *Server) handleConnect(ctx context.Context, _ *http.Request, buffer *buf
inbound.CanSpliceCopy = 1 inbound.CanSpliceCopy = 1
} }
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{ if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
Reader: reader, Reader: &buf.TimeoutWrapperReader{Reader: reader},
Writer: buf.NewWriter(conn)}, Writer: buf.NewWriter(conn)},
); err != nil { ); err != nil {
return errors.New("failed to dispatch request").Base(err) return errors.New("failed to dispatch request").Base(err)
+14 -87
View File
@@ -177,34 +177,20 @@ type VisionReader struct {
trafficState *TrafficState trafficState *TrafficState
ctx context.Context ctx context.Context
isUplink bool isUplink bool
conn net.Conn
input *bytes.Reader
rawInput *bytes.Buffer
ob *session.Outbound
// internal
directReadCounter stats.Counter
} }
func NewVisionReader(reader buf.Reader, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, ob *session.Outbound) *VisionReader { func NewVisionReader(reader buf.Reader, state *TrafficState, isUplink bool, context context.Context) *VisionReader {
return &VisionReader{ return &VisionReader{
Reader: reader, Reader: reader,
trafficState: trafficState, trafficState: state,
ctx: ctx, ctx: context,
isUplink: isUplink, isUplink: isUplink,
conn: conn,
input: input,
rawInput: rawInput,
ob: ob,
} }
} }
func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) { func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
buffer, err := w.Reader.ReadMultiBuffer() buffer, err := w.Reader.ReadMultiBuffer()
if buffer.IsEmpty() { if !buffer.IsEmpty() {
return buffer, err
}
var withinPaddingBuffers *bool var withinPaddingBuffers *bool
var remainingContent *int32 var remainingContent *int32
var remainingPadding *int32 var remainingPadding *int32
@@ -224,13 +210,6 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
switchToDirectCopy = &w.trafficState.Outbound.DownlinkReaderDirectCopy switchToDirectCopy = &w.trafficState.Outbound.DownlinkReaderDirectCopy
} }
if *switchToDirectCopy {
if w.directReadCounter != nil {
w.directReadCounter.Add(int64(buffer.Len()))
}
return buffer, err
}
if *withinPaddingBuffers || w.trafficState.NumberOfPacketToFilter > 0 { if *withinPaddingBuffers || w.trafficState.NumberOfPacketToFilter > 0 {
mb2 := make(buf.MultiBuffer, 0, len(buffer)) mb2 := make(buf.MultiBuffer, 0, len(buffer))
for _, b := range buffer { for _, b := range buffer {
@@ -254,31 +233,6 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
if w.trafficState.NumberOfPacketToFilter > 0 { if w.trafficState.NumberOfPacketToFilter > 0 {
XtlsFilterTls(buffer, w.trafficState, w.ctx) XtlsFilterTls(buffer, w.trafficState, w.ctx)
} }
if *switchToDirectCopy {
// XTLS Vision processes TLS-like conn's input and rawInput
if inputBuffer, err := buf.ReadFrom(w.input); err == nil && !inputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, inputBuffer)
}
if rawInputBuffer, err := buf.ReadFrom(w.rawInput); err == nil && !rawInputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
}
*w.input = bytes.Reader{} // release memory
w.input = nil
*w.rawInput = bytes.Buffer{} // release memory
w.rawInput = nil
if inbound := session.InboundFromContext(w.ctx); inbound != nil && inbound.Conn != nil {
if w.isUplink && inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
}
if !w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can have more than one ob
w.ob.CanSpliceCopy = 1
}
}
readerConn, readCounter, _ := UnwrapRawConn(w.conn)
w.directReadCounter = readCounter
w.Reader = buf.NewReader(readerConn)
} }
return buffer, err return buffer, err
} }
@@ -289,30 +243,26 @@ type VisionWriter struct {
buf.Writer buf.Writer
trafficState *TrafficState trafficState *TrafficState
ctx context.Context ctx context.Context
isUplink bool
conn net.Conn
ob *session.Outbound
// internal
writeOnceUserUUID []byte writeOnceUserUUID []byte
directWriteCounter stats.Counter isUplink bool
} }
func NewVisionWriter(writer buf.Writer, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, ob *session.Outbound) *VisionWriter { func NewVisionWriter(writer buf.Writer, state *TrafficState, isUplink bool, context context.Context) *VisionWriter {
w := make([]byte, len(trafficState.UserUUID)) w := make([]byte, len(state.UserUUID))
copy(w, trafficState.UserUUID) copy(w, state.UserUUID)
return &VisionWriter{ return &VisionWriter{
Writer: writer, Writer: writer,
trafficState: trafficState, trafficState: state,
ctx: ctx, ctx: context,
writeOnceUserUUID: w, writeOnceUserUUID: w,
isUplink: isUplink, isUplink: isUplink,
conn: conn,
ob: ob,
} }
} }
func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if w.trafficState.NumberOfPacketToFilter > 0 {
XtlsFilterTls(mb, w.trafficState, w.ctx)
}
var isPadding *bool var isPadding *bool
var switchToDirectCopy *bool var switchToDirectCopy *bool
if w.isUplink { if w.isUplink {
@@ -322,29 +272,6 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
isPadding = &w.trafficState.Inbound.IsPadding isPadding = &w.trafficState.Inbound.IsPadding
switchToDirectCopy = &w.trafficState.Inbound.DownlinkWriterDirectCopy switchToDirectCopy = &w.trafficState.Inbound.DownlinkWriterDirectCopy
} }
if *switchToDirectCopy {
if inbound := session.InboundFromContext(w.ctx); inbound != nil {
if !w.isUplink && inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
}
if w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 {
w.ob.CanSpliceCopy = 1
}
}
rawConn, _, writerCounter := UnwrapRawConn(w.conn)
w.Writer = buf.NewWriter(rawConn)
w.directWriteCounter = writerCounter
*switchToDirectCopy = false
}
if !mb.IsEmpty() && w.directWriteCounter != nil {
w.directWriteCounter.Add(int64(mb.Len()))
}
if w.trafficState.NumberOfPacketToFilter > 0 {
XtlsFilterTls(mb, w.trafficState, w.ctx)
}
if *isPadding { if *isPadding {
if len(mb) == 1 && mb[0] == nil { if len(mb) == 1 && mb[0] == nil {
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header
@@ -725,7 +652,7 @@ func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer sign
return nil return nil
} }
func IsRAWTransportWithoutSecurity(conn stat.Connection) bool { func IsRAWTransport(conn stat.Connection) bool {
iConn := conn iConn := conn
if statConn, ok := iConn.(*stat.CounterConnection); ok { if statConn, ok := iConn.(*stat.CounterConnection); ok {
iConn = statConn.Connection iConn = statConn.Connection
+2 -2
View File
@@ -75,7 +75,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
inbound.User = &protocol.MemoryUser{ inbound.User = &protocol.MemoryUser{
Level: s.config.UserLevel, Level: s.config.UserLevel,
} }
if !proxy.IsRAWTransportWithoutSecurity(conn) { if !proxy.IsRAWTransport(conn) {
inbound.CanSpliceCopy = 3 inbound.CanSpliceCopy = 3
} }
@@ -161,7 +161,7 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche
inbound.CanSpliceCopy = 1 inbound.CanSpliceCopy = 1
} }
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{ if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
Reader: reader, Reader: &buf.TimeoutWrapperReader{Reader: reader},
Writer: buf.NewWriter(conn)}, Writer: buf.NewWriter(conn)},
); err != nil { ); err != nil {
return errors.New("failed to dispatch request").Base(err) return errors.New("failed to dispatch request").Base(err)
+1
View File
@@ -253,6 +253,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, sessionPolicy policy.Sess
defer cancel() defer cancel()
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
defer timer.SetTimeout(0) defer timer.SetTimeout(0)
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) { udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
udpPayload := packet.Payload udpPayload := packet.Payload
if udpPayload.UDP == nil { if udpPayload.UDP == nil {
-3
View File
@@ -20,7 +20,6 @@ func (a *Account) AsAccount() (protocol.Account, error) {
Encryption: a.Encryption, // needs parser here? Encryption: a.Encryption, // needs parser here?
XorMode: a.XorMode, XorMode: a.XorMode,
Seconds: a.Seconds, Seconds: a.Seconds,
Padding: a.Padding,
}, nil }, nil
} }
@@ -34,7 +33,6 @@ type MemoryAccount struct {
Encryption string Encryption string
XorMode uint32 XorMode uint32
Seconds uint32 Seconds uint32
Padding string
} }
// Equals implements protocol.Account.Equals(). // Equals implements protocol.Account.Equals().
@@ -53,6 +51,5 @@ func (a *MemoryAccount) ToProto() proto.Message {
Encryption: a.Encryption, Encryption: a.Encryption,
XorMode: a.XorMode, XorMode: a.XorMode,
Seconds: a.Seconds, Seconds: a.Seconds,
Padding: a.Padding,
} }
} }
+7 -17
View File
@@ -32,7 +32,6 @@ type Account struct {
Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"` Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"` XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"` Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
Padding string `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"`
} }
func (x *Account) Reset() { func (x *Account) Reset() {
@@ -100,19 +99,12 @@ func (x *Account) GetSeconds() uint32 {
return 0 return 0
} }
func (x *Account) GetPadding() string {
if x != nil {
return x.Padding
}
return ""
}
var File_proxy_vless_account_proto protoreflect.FileDescriptor var File_proxy_vless_account_proto protoreflect.FileDescriptor
var file_proxy_vless_account_proto_rawDesc = []byte{ var file_proxy_vless_account_proto_rawDesc = []byte{
0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63, 0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x9b, 0x01, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x81, 0x01,
0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f,
0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a,
@@ -121,14 +113,12 @@ var file_proxy_vless_account_proto_rawDesc = []byte{
0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e,
0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64,
0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x73, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72,
0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74,
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65,
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
-1
View File
@@ -15,5 +15,4 @@ message Account {
string encryption = 3; string encryption = 3;
uint32 xorMode = 4; uint32 xorMode = 4;
uint32 seconds = 5; uint32 seconds = 5;
string padding = 6;
} }
+5 -6
View File
@@ -3,12 +3,10 @@ package encoding
import ( import (
"context" "context"
"io" "io"
"net"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/proxy/vless" "github.com/xtls/xray-core/proxy/vless"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@@ -63,14 +61,15 @@ func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) {
} }
// EncodeBodyAddons returns a Writer that auto-encrypt content written by caller. // EncodeBodyAddons returns a Writer that auto-encrypt content written by caller.
func EncodeBodyAddons(writer buf.Writer, request *protocol.RequestHeader, requestAddons *Addons, state *proxy.TrafficState, isUplink bool, context context.Context, conn net.Conn, ob *session.Outbound) buf.Writer { func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons, state *proxy.TrafficState, isUplink bool, context context.Context) buf.Writer {
if request.Command == protocol.RequestCommandUDP { if request.Command == protocol.RequestCommandUDP {
return NewMultiLengthPacketWriter(writer) return NewMultiLengthPacketWriter(writer.(buf.Writer))
} }
w := buf.NewWriter(writer)
if requestAddons.Flow == vless.XRV { if requestAddons.Flow == vless.XRV {
return proxy.NewVisionWriter(writer, state, isUplink, context, conn, ob) w = proxy.NewVisionWriter(w, state, isUplink, context)
} }
return writer return w
} }
// DecodeBodyAddons returns a Reader from which caller can fetch decrypted body. // DecodeBodyAddons returns a Reader from which caller can fetch decrypted body.
+73 -2
View File
@@ -1,6 +1,7 @@
package encoding package encoding
import ( import (
"bytes"
"context" "context"
"io" "io"
@@ -10,6 +11,7 @@ import (
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/proxy/vless" "github.com/xtls/xray-core/proxy/vless"
) )
@@ -169,8 +171,8 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A
return responseAddons, nil return responseAddons, nil
} }
// XtlsRead can switch to splice copy // XtlsRead filter and read xtls protocol
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, trafficState *proxy.TrafficState, isUplink bool, ctx context.Context) error { func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, peerCache *[]byte, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
err := func() error { err := func() error {
for { for {
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy { if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
@@ -179,11 +181,80 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer,
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil { if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
writerConn = inbound.Conn writerConn = inbound.Conn
inTimer = inbound.Timer inTimer = inbound.Timer
if isUplink && inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
}
if !isUplink && ob != nil && ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can change
ob.CanSpliceCopy = 1
}
} }
return proxy.CopyRawConnIfExist(ctx, conn, writerConn, writer, timer, inTimer) return proxy.CopyRawConnIfExist(ctx, conn, writerConn, writer, timer, inTimer)
} }
buffer, err := reader.ReadMultiBuffer() buffer, err := reader.ReadMultiBuffer()
if !buffer.IsEmpty() { if !buffer.IsEmpty() {
timer.Update()
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
// XTLS Vision processes struct Encryption Conn's peerCache or TLS Conn's input and rawInput
if peerCache != nil {
if len(*peerCache) != 0 {
buffer = buf.MergeBytes(buffer, *peerCache)
}
} else {
if inputBuffer, err := buf.ReadFrom(input); err == nil {
if !inputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, inputBuffer)
}
}
if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil {
if !rawInputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
}
}
}
}
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
return werr
}
}
if err != nil {
return err
}
}
}()
if err != nil && errors.Cause(err) != io.EOF {
return err
}
return nil
}
// XtlsWrite filter and write xtls protocol
func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
err := func() error {
var ct stats.Counter
for {
buffer, err := reader.ReadMultiBuffer()
if isUplink && trafficState.Outbound.UplinkWriterDirectCopy || !isUplink && trafficState.Inbound.DownlinkWriterDirectCopy {
if inbound := session.InboundFromContext(ctx); inbound != nil {
if !isUplink && inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
}
if isUplink && ob != nil && ob.CanSpliceCopy == 2 {
ob.CanSpliceCopy = 1
}
}
rawConn, _, writerCounter := proxy.UnwrapRawConn(conn)
writer = buf.NewWriter(rawConn)
ct = writerCounter
if isUplink {
trafficState.Outbound.UplinkWriterDirectCopy = false
} else {
trafficState.Inbound.DownlinkWriterDirectCopy = false
}
}
if !buffer.IsEmpty() {
if ct != nil {
ct.Add(int64(buffer.Len()))
}
timer.Update() timer.Update()
if werr := writer.WriteMultiBuffer(buffer); werr != nil { if werr := writer.WriteMultiBuffer(buffer); werr != nil {
return werr return werr
+10 -17
View File
@@ -10,6 +10,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/xtls/xray-core/common/crypto"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"lukechampine.com/blake3" "lukechampine.com/blake3"
@@ -22,8 +23,6 @@ type ClientInstance struct {
RelaysLength int RelaysLength int
XorMode uint32 XorMode uint32
Seconds uint32 Seconds uint32
PaddingLens [][3]int
PaddingGaps [][3]int
RWLock sync.RWMutex RWLock sync.RWMutex
Expire time.Time Expire time.Time
@@ -31,13 +30,15 @@ type ClientInstance struct {
Ticket []byte Ticket []byte
} }
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) { func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
if i.NfsPKeys != nil { if i.NfsPKeys != nil {
return errors.New("already initialized") err = errors.New("already initialized")
return
} }
l := len(nfsPKeysBytes) l := len(nfsPKeysBytes)
if l == 0 { if l == 0 {
return errors.New("empty nfsPKeysBytes") err = errors.New("empty nfsPKeysBytes")
return
} }
i.NfsPKeys = make([]any, l) i.NfsPKeys = make([]any, l)
i.NfsPKeysBytes = nfsPKeysBytes i.NfsPKeysBytes = nfsPKeysBytes
@@ -59,7 +60,7 @@ func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32, p
i.RelaysLength -= 32 i.RelaysLength -= 32
i.XorMode = xorMode i.XorMode = xorMode
i.Seconds = seconds i.Seconds = seconds
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps) return
} }
func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) { func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
@@ -70,7 +71,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
ivAndRealysLength := 16 + i.RelaysLength ivAndRealysLength := 16 + i.RelaysLength
pfsKeyExchangeLength := 18 + 1184 + 32 + 16 pfsKeyExchangeLength := 18 + 1184 + 32 + 16
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps) paddingLength := int(crypto.RandBetween(100, 1000))
clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength) clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)
iv := clientHello[:16] iv := clientHello[:16]
@@ -139,18 +140,10 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil) nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil) nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
paddingLens[0] = ivAndRealysLength + pfsKeyExchangeLength + paddingLens[0] if _, err := conn.Write(clientHello); err != nil {
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
if l > 0 {
if _, err := conn.Write(clientHello[:l]); err != nil {
return nil, err return nil, err
} }
clientHello = clientHello[l:] // padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
}
if len(paddingGaps) > i {
time.Sleep(paddingGaps[i])
}
}
encryptedPfsPublicKey := make([]byte, 1088+32+16) encryptedPfsPublicKey := make([]byte, 1088+32+16)
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil { if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
+15 -76
View File
@@ -7,12 +7,10 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/xtls/xray-core/common/crypto"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20poly1305"
"lukechampine.com/blake3" "lukechampine.com/blake3"
@@ -33,14 +31,15 @@ type CommonConn struct {
AEAD *AEAD AEAD *AEAD
PeerAEAD *AEAD PeerAEAD *AEAD
PeerPadding []byte PeerPadding []byte
rawInput bytes.Buffer PeerInBytes []byte
input bytes.Reader PeerCache []byte
} }
func NewCommonConn(conn net.Conn, useAES bool) *CommonConn { func NewCommonConn(conn net.Conn, useAES bool) *CommonConn {
return &CommonConn{ return &CommonConn{
Conn: conn, Conn: conn,
UseAES: useAES, UseAES: useAES,
PeerInBytes: make([]byte, 5+17000), // no need to use sync.Pool, because we are always reading
} }
} }
@@ -100,14 +99,16 @@ func (c *CommonConn) Read(b []byte) (int, error) {
} }
c.PeerPadding = nil c.PeerPadding = nil
} }
if c.input.Len() > 0 { if len(c.PeerCache) > 0 {
return c.input.Read(b) n := copy(b, c.PeerCache)
c.PeerCache = c.PeerCache[n:]
return n, nil
} }
peerHeader := [5]byte{} peerHeader := c.PeerInBytes[:5]
if _, err := io.ReadFull(c.Conn, peerHeader[:]); err != nil { if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
return 0, err return 0, err
} }
l, err := DecodeHeader(peerHeader[:]) // l: 17~17000 l, err := DecodeHeader(c.PeerInBytes[:5]) // l: 17~17000
if err != nil { if err != nil {
if c.Client != nil && strings.Contains(err.Error(), "invalid header: ") { // client's 0-RTT if c.Client != nil && strings.Contains(err.Error(), "invalid header: ") { // client's 0-RTT
c.Client.RWLock.Lock() c.Client.RWLock.Lock()
@@ -120,10 +121,7 @@ func (c *CommonConn) Read(b []byte) (int, error) {
return 0, err return 0, err
} }
c.Client = nil c.Client = nil
if c.rawInput.Cap() < l { peerData := c.PeerInBytes[5 : 5+l]
c.rawInput.Grow(l) // no need to use sync.Pool, because we are always reading
}
peerData := c.rawInput.Bytes()[:l]
if _, err := io.ReadFull(c.Conn, peerData); err != nil { if _, err := io.ReadFull(c.Conn, peerData); err != nil {
return 0, err return 0, err
} }
@@ -133,9 +131,9 @@ func (c *CommonConn) Read(b []byte) (int, error) {
} }
var newAEAD *AEAD var newAEAD *AEAD
if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) { if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {
newAEAD = NewAEAD(append(peerHeader[:], peerData...), c.UnitedKey, c.UseAES) newAEAD = NewAEAD(c.PeerInBytes[:5+l], c.UnitedKey, c.UseAES)
} }
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader[:]) _, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader)
if newAEAD != nil { if newAEAD != nil {
c.PeerAEAD = newAEAD c.PeerAEAD = newAEAD
} }
@@ -143,7 +141,7 @@ func (c *CommonConn) Read(b []byte) (int, error) {
return 0, err return 0, err
} }
if len(dst) > len(b) { if len(dst) > len(b) {
c.input.Reset(dst[copy(b, dst):]) c.PeerCache = dst[copy(b, dst):]
dst = b // for len(dst) dst = b // for len(dst)
} }
return len(dst), nil return len(dst), nil
@@ -215,66 +213,7 @@ func DecodeHeader(h []byte) (l int, err error) {
l = 0 l = 0
} }
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
err = errors.New("invalid header: " + fmt.Sprintf("%v", h[:5])) // DO NOT CHANGE: relied by client's Read() err = errors.New("invalid header: ", fmt.Sprintf("%v", h[:5])) // DO NOT CHANGE: relied by client's Read()
}
return
}
func ParsePadding(padding string, paddingLens, paddingGaps *[][3]int) (err error) {
if padding == "" {
return
}
maxLen := 0
for i, s := range strings.Split(padding, ".") {
x := strings.Split(s, "-")
if len(x) < 3 || x[0] == "" || x[1] == "" || x[2] == "" {
return errors.New("invalid padding lenth/gap parameter: " + s)
}
y := [3]int{}
if y[0], err = strconv.Atoi(x[0]); err != nil {
return
}
if y[1], err = strconv.Atoi(x[1]); err != nil {
return
}
if y[2], err = strconv.Atoi(x[2]); err != nil {
return
}
if i == 0 && (y[0] < 100 || y[1] < 18+17 || y[2] < 18+17) {
return errors.New("first padding length must not be smaller than 35")
}
if i%2 == 0 {
*paddingLens = append(*paddingLens, y)
maxLen += max(y[1], y[2])
} else {
*paddingGaps = append(*paddingGaps, y)
}
}
if maxLen > 18+65535 {
return errors.New("total padding length must not be larger than 65553")
}
return
}
func CreatPadding(paddingLens, paddingGaps [][3]int) (length int, lens []int, gaps []time.Duration) {
if len(paddingLens) == 0 {
paddingLens = [][3]int{{100, 111, 1111}, {50, 0, 3333}}
paddingGaps = [][3]int{{75, 0, 111}}
}
for _, y := range paddingLens {
l := 0
if y[0] >= int(crypto.RandBetween(0, 100)) {
l = int(crypto.RandBetween(int64(y[1]), int64(y[2])))
}
lens = append(lens, l)
length += l
}
for _, y := range paddingGaps {
g := 0
if y[0] >= int(crypto.RandBetween(0, 100)) {
g = int(crypto.RandBetween(int64(y[1]), int64(y[2])))
}
gaps = append(gaps, time.Duration(g)*time.Millisecond)
} }
return return
} }
+32 -73
View File
@@ -18,6 +18,7 @@ import (
) )
type ServerSession struct { type ServerSession struct {
Expire time.Time
PfsKey []byte PfsKey []byte
NfsKeys sync.Map NfsKeys sync.Map
} }
@@ -28,25 +29,22 @@ type ServerInstance struct {
Hash32s [][32]byte Hash32s [][32]byte
RelaysLength int RelaysLength int
XorMode uint32 XorMode uint32
SecondsFrom int64 Seconds uint32
SecondsTo int64
PaddingLens [][3]int
PaddingGaps [][3]int
RWLock sync.RWMutex RWLock sync.RWMutex
Closed bool
Lasts map[int64][16]byte
Tickets [][16]byte
Sessions map[[16]byte]*ServerSession Sessions map[[16]byte]*ServerSession
Closed bool
} }
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode uint32, secondsFrom, secondsTo int64, padding string) (err error) { func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
if i.NfsSKeys != nil { if i.NfsSKeys != nil {
return errors.New("already initialized") err = errors.New("already initialized")
return
} }
l := len(nfsSKeysBytes) l := len(nfsSKeysBytes)
if l == 0 { if l == 0 {
return errors.New("empty nfsSKeysBytes") err = errors.New("empty nfsSKeysBytes")
return
} }
i.NfsSKeys = make([]any, l) i.NfsSKeys = make([]any, l)
i.NfsPKeysBytes = make([][]byte, l) i.NfsPKeysBytes = make([][]byte, l)
@@ -69,15 +67,8 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode uint32, secondsFro
} }
i.RelaysLength -= 32 i.RelaysLength -= 32
i.XorMode = xorMode i.XorMode = xorMode
i.SecondsFrom = secondsFrom if seconds > 0 {
i.SecondsTo = secondsTo i.Seconds = seconds
err = ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
if err != nil {
return
}
if i.SecondsFrom > 0 || i.SecondsTo > 0 {
i.Lasts = make(map[int64][16]byte)
i.Tickets = make([][16]byte, 0, 1024)
i.Sessions = make(map[[16]byte]*ServerSession) i.Sessions = make(map[[16]byte]*ServerSession)
go func() { go func() {
for { for {
@@ -87,17 +78,10 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode uint32, secondsFro
i.RWLock.Unlock() i.RWLock.Unlock()
return return
} }
minute := time.Now().Unix() / 60 now := time.Now()
last := i.Lasts[minute] for ticket, session := range i.Sessions {
delete(i.Lasts, minute) if now.After(session.Expire) {
delete(i.Lasts, minute-1) // for insurance
if last != [16]byte{} {
for j, ticket := range i.Tickets {
delete(i.Sessions, ticket) delete(i.Sessions, ticket)
if ticket == last {
i.Tickets = i.Tickets[j+1:]
break
}
} }
} }
i.RWLock.Unlock() i.RWLock.Unlock()
@@ -114,7 +98,7 @@ func (i *ServerInstance) Close() (err error) {
return return
} }
func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) { func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
if i.NfsSKeys == nil { if i.NfsSKeys == nil {
return nil, errors.New("uninitialized") return nil, errors.New("uninitialized")
} }
@@ -124,9 +108,6 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn
if _, err := io.ReadFull(conn, ivAndRelays); err != nil { if _, err := io.ReadFull(conn, ivAndRelays); err != nil {
return nil, err return nil, err
} }
if fallback != nil {
*fallback = append(*fallback, ivAndRelays...)
}
iv := ivAndRelays[:16] iv := ivAndRelays[:16]
relays := ivAndRelays[16:] relays := ivAndRelays[16:]
var nfsKey []byte var nfsKey []byte
@@ -140,16 +121,13 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn
index = 1088 index = 1088
} }
if i.XorMode > 0 { if i.XorMode > 0 {
NewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // we don't use buggy elligator2, because we have PSK :) NewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // we don't use buggy elligator, because we have PSK :)
} }
if k, ok := k.(*ecdh.PrivateKey); ok { if k, ok := k.(*ecdh.PrivateKey); ok {
publicKey, err := ecdh.X25519().NewPublicKey(relays[:index]) publicKey, err := ecdh.X25519().NewPublicKey(relays[:index])
if err != nil { if err != nil {
return nil, err return nil, err
} }
if publicKey.Bytes()[31] > 127 { // we just don't want the observer can change even one bit without breaking the connection, though it has nothing to do with security
return nil, errors.New("the highest bit of the last byte of the peer-sent X25519 public key is not 0")
}
nfsKey, err = k.ECDH(publicKey) nfsKey, err = k.ECDH(publicKey)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -179,9 +157,6 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn
if _, err := io.ReadFull(conn, encryptedLength); err != nil { if _, err := io.ReadFull(conn, encryptedLength); err != nil {
return nil, err return nil, err
} }
if fallback != nil {
*fallback = append(*fallback, encryptedLength...)
}
decryptedLength := make([]byte, 2) decryptedLength := make([]byte, 2)
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil { if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
c.UseAES = !c.UseAES c.UseAES = !c.UseAES
@@ -190,13 +165,10 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn
return nil, err return nil, err
} }
} }
if fallback != nil {
*fallback = nil
}
length := DecodeLength(decryptedLength) length := DecodeLength(decryptedLength)
if length == 32 { if length == 32 {
if i.SecondsFrom == 0 && i.SecondsTo == 0 { if i.Seconds == 0 {
return nil, errors.New("0-RTT is not allowed") return nil, errors.New("0-RTT is not allowed")
} }
encryptedTicket := make([]byte, 32) encryptedTicket := make([]byte, 32)
@@ -211,7 +183,7 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn
s := i.Sessions[[16]byte(ticket)] s := i.Sessions[[16]byte(ticket)]
i.RWLock.RUnlock() i.RWLock.RUnlock()
if s == nil { if s == nil {
noises := make([]byte, crypto.RandBetween(1279, 2279)) // matches 1-RTT's server hello length for "random", though it is not important, just for example noises := make([]byte, crypto.RandBetween(1268, 2268)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
var err error var err error
for err == nil { for err == nil {
rand.Read(noises) rand.Read(noises)
@@ -265,45 +237,32 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn
c.UnitedKey = append(pfsKey, nfsKey...) c.UnitedKey = append(pfsKey, nfsKey...)
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES) c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES) c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
ticket := make([]byte, 16)
ticket := [16]byte{} rand.Read(ticket)
rand.Read(ticket[:]) copy(ticket, EncodeLength(int(i.Seconds*4/5)))
var seconds int64
if i.SecondsTo == 0 {
seconds = i.SecondsFrom * crypto.RandBetween(50, 100) / 100
} else {
seconds = crypto.RandBetween(i.SecondsFrom, i.SecondsTo)
}
copy(ticket[:], EncodeLength(int(seconds)))
if seconds > 0 {
i.RWLock.Lock()
i.Lasts[(time.Now().Unix()+max(i.SecondsFrom, i.SecondsTo))/60+2] = ticket
i.Tickets = append(i.Tickets, ticket)
i.Sessions[ticket] = &ServerSession{PfsKey: pfsKey}
i.RWLock.Unlock()
}
pfsKeyExchangeLength := 1088 + 32 + 16 pfsKeyExchangeLength := 1088 + 32 + 16
encryptedTicketLength := 32 encryptedTicketLength := 32
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps) paddingLength := int(crypto.RandBetween(100, 1000))
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength) serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil) nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket[:], nil) c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:] padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil) c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil) c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
paddingLens[0] = pfsKeyExchangeLength + encryptedTicketLength + paddingLens[0] if _, err := conn.Write(serverHello); err != nil {
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
if l > 0 {
if _, err := conn.Write(serverHello[:l]); err != nil {
return nil, err return nil, err
} }
serverHello = serverHello[l:] // padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
}
if len(paddingGaps) > i { if i.Seconds > 0 {
time.Sleep(paddingGaps[i]) i.RWLock.Lock()
i.Sessions[[16]byte(ticket)] = &ServerSession{
Expire: time.Now().Add(time.Duration(i.Seconds) * time.Second),
PfsKey: pfsKey,
} }
i.RWLock.Unlock()
} }
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern // important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
@@ -322,7 +281,7 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn
} }
if i.XorMode == 2 { if i.XorMode == 2 {
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, ticket[:]), NewCTR(c.UnitedKey, iv), 0, 0) c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, ticket), NewCTR(c.UnitedKey, iv), 0, 0)
} }
return c, nil return c, nil
} }
+14 -34
View File
@@ -115,9 +115,7 @@ type Config struct {
Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"` Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"`
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"` Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"` XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
SecondsFrom int64 `protobuf:"varint,5,opt,name=seconds_from,json=secondsFrom,proto3" json:"seconds_from,omitempty"` Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
SecondsTo int64 `protobuf:"varint,6,opt,name=seconds_to,json=secondsTo,proto3" json:"seconds_to,omitempty"`
Padding string `protobuf:"bytes,7,opt,name=padding,proto3" json:"padding,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -178,27 +176,13 @@ func (x *Config) GetXorMode() uint32 {
return 0 return 0
} }
func (x *Config) GetSecondsFrom() int64 { func (x *Config) GetSeconds() uint32 {
if x != nil { if x != nil {
return x.SecondsFrom return x.Seconds
} }
return 0 return 0
} }
func (x *Config) GetSecondsTo() int64 {
if x != nil {
return x.SecondsTo
}
return 0
}
func (x *Config) GetPadding() string {
if x != nil {
return x.Padding
}
return ""
}
var File_proxy_vless_inbound_config_proto protoreflect.FileDescriptor var File_proxy_vless_inbound_config_proto protoreflect.FileDescriptor
var file_proxy_vless_inbound_config_proto_rawDesc = []byte{ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
@@ -215,7 +199,7 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65,
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0x96, 0x02, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xd4, 0x01,
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
@@ -227,20 +211,16 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x12, 0x18, 0x0a, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x12, 0x18, 0x0a, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x0d, 0x52, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65,
0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63,
0x52, 0x0b, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x1d, 0x0a, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x0a, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62,
0x03, 0x52, 0x09, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x54, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e,
0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f,
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f,
0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50,
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
+1 -3
View File
@@ -23,7 +23,5 @@ message Config {
string decryption = 3; string decryption = 3;
uint32 xorMode = 4; uint32 xorMode = 4;
int64 seconds_from = 5; uint32 seconds = 5;
int64 seconds_to = 6;
string padding = 7;
} }
+88 -21
View File
@@ -31,7 +31,6 @@ import (
"github.com/xtls/xray-core/proxy/vless" "github.com/xtls/xray-core/proxy/vless"
"github.com/xtls/xray-core/proxy/vless/encoding" "github.com/xtls/xray-core/proxy/vless/encoding"
"github.com/xtls/xray-core/proxy/vless/encryption" "github.com/xtls/xray-core/proxy/vless/encryption"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/reality"
"github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/internet/tls"
@@ -93,7 +92,7 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val
nfsSKeysBytes = append(nfsSKeysBytes, b) nfsSKeysBytes = append(nfsSKeysBytes, b)
} }
handler.decryption = &encryption.ServerInstance{} handler.decryption = &encryption.ServerInstance{}
if err := handler.decryption.Init(nfsSKeysBytes, config.XorMode, config.SecondsFrom, config.SecondsTo, config.Padding); err != nil { if err := handler.decryption.Init(nfsSKeysBytes, config.XorMode, config.Seconds); err != nil {
return nil, errors.New("failed to use decryption").Base(err).AtError() return nil, errors.New("failed to use decryption").Base(err).AtError()
} }
} }
@@ -221,7 +220,8 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
if h.decryption != nil { if h.decryption != nil {
var err error var err error
if connection, err = h.decryption.Handshake(connection, nil); err != nil { connection, err = h.decryption.Handshake(connection)
if err != nil {
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo() return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
} }
} }
@@ -491,6 +491,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
// Flow: requestAddons.Flow, // Flow: requestAddons.Flow,
} }
var peerCache *[]byte
var input *bytes.Reader var input *bytes.Reader
var rawInput *bytes.Buffer var rawInput *bytes.Buffer
switch requestAddons.Flow { switch requestAddons.Flow {
@@ -503,15 +504,16 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
case protocol.RequestCommandMux: case protocol.RequestCommandMux:
fallthrough // we will break Mux connections that contain TCP requests fallthrough // we will break Mux connections that contain TCP requests
case protocol.RequestCommandTCP: case protocol.RequestCommandTCP:
if serverConn, ok := connection.(*encryption.CommonConn); ok {
peerCache = &serverConn.PeerCache
if _, ok := serverConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
}
break
}
var t reflect.Type var t reflect.Type
var p uintptr var p uintptr
if commonConn, ok := connection.(*encryption.CommonConn); ok { if tlsConn, ok := iConn.(*tls.Conn); ok {
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransportWithoutSecurity(iConn) {
inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport / another securityConn should not be penetrated
}
t = reflect.TypeOf(commonConn).Elem()
p = uintptr(unsafe.Pointer(commonConn))
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
if tlsConn.ConnectionState().Version != gotls.VersionTLS13 { if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning() return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning()
} }
@@ -552,24 +554,89 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
ctx = session.ContextWithAllowedNetwork(ctx, net.Network_UDP) ctx = session.ContextWithAllowedNetwork(ctx, net.Network_UDP)
} }
trafficState := proxy.NewTrafficState(userSentID) sessionPolicy = h.policyManager.ForLevel(request.User.Level)
clientReader := encoding.DecodeBodyAddons(reader, request, requestAddons) ctx, cancel := context.WithCancel(ctx)
if requestAddons.Flow == vless.XRV { timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
clientReader = proxy.NewVisionReader(clientReader, trafficState, true, ctx, connection, input, rawInput, nil) inbound.Timer = timer
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
link, err := dispatcher.Dispatch(ctx, request.Destination())
if err != nil {
return errors.New("failed to dispatch request to ", request.Destination()).Base(err).AtWarning()
} }
serverReader := link.Reader // .(*pipe.Reader)
serverWriter := link.Writer // .(*pipe.Writer)
trafficState := proxy.NewTrafficState(userSentID)
postRequest := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
// default: clientReader := reader
clientReader := encoding.DecodeBodyAddons(reader, request, requestAddons)
var err error
if requestAddons.Flow == vless.XRV {
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
clientReader = proxy.NewVisionReader(clientReader, trafficState, true, ctx1)
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, peerCache, input, rawInput, trafficState, nil, true, ctx1)
} else {
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
}
if err != nil {
return errors.New("failed to transfer request payload").Base(err).AtInfo()
}
return nil
}
getResponse := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
bufferWriter := buf.NewBufferedWriter(buf.NewWriter(connection)) bufferWriter := buf.NewBufferedWriter(buf.NewWriter(connection))
if err := encoding.EncodeResponseHeader(bufferWriter, request, responseAddons); err != nil { if err := encoding.EncodeResponseHeader(bufferWriter, request, responseAddons); err != nil {
return errors.New("failed to encode response header").Base(err).AtWarning() return errors.New("failed to encode response header").Base(err).AtWarning()
} }
clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, false, ctx, connection, nil)
bufferWriter.SetFlushNext()
if err := dispatcher.DispatchLink(ctx, request.Destination(), &transport.Link{ // default: clientWriter := bufferWriter
Reader: clientReader, clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, false, ctx)
Writer: clientWriter}, multiBuffer, err1 := serverReader.ReadMultiBuffer()
); err != nil { if err1 != nil {
return errors.New("failed to dispatch request").Base(err) return err1 // ...
} }
if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil {
return err // ...
}
// Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
if err := bufferWriter.SetBuffered(false); err != nil {
return errors.New("failed to write A response payload").Base(err).AtWarning()
}
var err error
if requestAddons.Flow == vless.XRV {
err = encoding.XtlsWrite(serverReader, clientWriter, timer, connection, trafficState, nil, false, ctx)
} else {
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
}
if err != nil {
return errors.New("failed to transfer response payload").Base(err).AtInfo()
}
// Indicates the end of response payload.
switch responseAddons.Flow {
default:
}
return nil
}
if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), getResponse); err != nil {
common.Interrupt(serverReader)
common.Interrupt(serverWriter)
return errors.New("connection ends").Base(err).AtInfo()
}
return nil return nil
} }
+21 -13
View File
@@ -77,7 +77,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
nfsPKeysBytes = append(nfsPKeysBytes, b) nfsPKeysBytes = append(nfsPKeysBytes, b)
} }
handler.encryption = &encryption.ClientInstance{} handler.encryption = &encryption.ClientInstance{}
if err := handler.encryption.Init(nfsPKeysBytes, a.XorMode, a.Seconds, a.Padding); err != nil { if err := handler.encryption.Init(nfsPKeysBytes, a.XorMode, a.Seconds); err != nil {
return nil, errors.New("failed to use encryption").Base(err).AtError() return nil, errors.New("failed to use encryption").Base(err).AtError()
} }
} }
@@ -118,7 +118,8 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
if h.encryption != nil { if h.encryption != nil {
var err error var err error
if conn, err = h.encryption.Handshake(conn); err != nil { conn, err = h.encryption.Handshake(conn)
if err != nil {
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo() return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
} }
} }
@@ -145,6 +146,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
Flow: account.Flow, Flow: account.Flow,
} }
var peerCache *[]byte
var input *bytes.Reader var input *bytes.Reader
var rawInput *bytes.Buffer var rawInput *bytes.Buffer
allowUDP443 := false allowUDP443 := false
@@ -163,15 +165,16 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
case protocol.RequestCommandMux: case protocol.RequestCommandMux:
fallthrough // let server break Mux connections that contain TCP requests fallthrough // let server break Mux connections that contain TCP requests
case protocol.RequestCommandTCP: case protocol.RequestCommandTCP:
if clientConn, ok := conn.(*encryption.CommonConn); ok {
peerCache = &clientConn.PeerCache
if _, ok := clientConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
}
break
}
var t reflect.Type var t reflect.Type
var p uintptr var p uintptr
if commonConn, ok := conn.(*encryption.CommonConn); ok { if tlsConn, ok := iConn.(*tls.Conn); ok {
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransportWithoutSecurity(iConn) {
ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport / another securityConn should not be penetrated
}
t = reflect.TypeOf(commonConn).Elem()
p = uintptr(unsafe.Pointer(commonConn))
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
t = reflect.TypeOf(tlsConn.Conn).Elem() t = reflect.TypeOf(tlsConn.Conn).Elem()
p = uintptr(unsafe.Pointer(tlsConn.Conn)) p = uintptr(unsafe.Pointer(tlsConn.Conn))
} else if utlsConn, ok := iConn.(*tls.UConn); ok { } else if utlsConn, ok := iConn.(*tls.UConn); ok {
@@ -225,7 +228,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
} }
// default: serverWriter := bufferWriter // default: serverWriter := bufferWriter
serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, true, ctx, conn, ob) serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, true, ctx)
if request.Command == protocol.RequestCommandMux && request.Port == 666 { if request.Command == protocol.RequestCommandMux && request.Port == 666 {
serverWriter = xudp.NewPacketWriter(serverWriter, target, xudp.GetGlobalID(ctx)) serverWriter = xudp.NewPacketWriter(serverWriter, target, xudp.GetGlobalID(ctx))
} }
@@ -253,6 +256,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
return errors.New("failed to write A request payload").Base(err).AtWarning() return errors.New("failed to write A request payload").Base(err).AtWarning()
} }
var err error
if requestAddons.Flow == vless.XRV { if requestAddons.Flow == vless.XRV {
if tlsConn, ok := iConn.(*tls.Conn); ok { if tlsConn, ok := iConn.(*tls.Conn); ok {
if tlsConn.ConnectionState().Version != gotls.VersionTLS13 { if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
@@ -263,8 +267,12 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, utlsConn.ConnectionState().Version).AtWarning() return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, utlsConn.ConnectionState().Version).AtWarning()
} }
} }
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ob, true, ctx1)
} else {
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
} }
err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
if err != nil { if err != nil {
return errors.New("failed to transfer request payload").Base(err).AtInfo() return errors.New("failed to transfer request payload").Base(err).AtInfo()
} }
@@ -287,7 +295,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
// default: serverReader := buf.NewReader(conn) // default: serverReader := buf.NewReader(conn)
serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons) serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons)
if requestAddons.Flow == vless.XRV { if requestAddons.Flow == vless.XRV {
serverReader = proxy.NewVisionReader(serverReader, trafficState, false, ctx, conn, input, rawInput, ob) serverReader = proxy.NewVisionReader(serverReader, trafficState, false, ctx)
} }
if request.Command == protocol.RequestCommandMux && request.Port == 666 { if request.Command == protocol.RequestCommandMux && request.Port == 666 {
if requestAddons.Flow == vless.XRV { if requestAddons.Flow == vless.XRV {
@@ -298,7 +306,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
} }
if requestAddons.Flow == vless.XRV { if requestAddons.Flow == vless.XRV {
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, trafficState, false, ctx) err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, peerCache, input, rawInput, trafficState, ob, false, ctx)
} else { } else {
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)) err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
+2 -2
View File
@@ -10,10 +10,10 @@ import (
"hash/crc32" "hash/crc32"
"io" "io"
"math" "math"
"time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/antireplay" "github.com/xtls/xray-core/common/antireplay"
"github.com/xtls/xray-core/proxy/vmess/vtime"
) )
var ( var (
@@ -105,7 +105,7 @@ func (a *AuthIDDecoderHolder) Match(authID [16]byte) (interface{}, error) {
continue continue
} }
if math.Abs(math.Abs(float64(t))-float64(vtime.Now().Unix())) > 120 { if math.Abs(math.Abs(float64(t))-float64(time.Now().Unix())) > 120 {
continue continue
} }
+2 -2
View File
@@ -5,14 +5,14 @@ import (
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"io" "io"
"time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/crypto"
"github.com/xtls/xray-core/proxy/vmess/vtime"
) )
func SealVMessAEADHeader(key [16]byte, data []byte) []byte { func SealVMessAEADHeader(key [16]byte, data []byte) []byte {
generatedAuthID := CreateAuthID(key[:], vtime.Now().Unix()) generatedAuthID := CreateAuthID(key[:], time.Now().Unix())
connectionNonce := make([]byte, 8) connectionNonce := make([]byte, 8)
if _, err := io.ReadFull(rand.Reader, connectionNonce); err != nil { if _, err := io.ReadFull(rand.Reader, connectionNonce); err != nil {
-74
View File
@@ -1,74 +0,0 @@
package vtime
import (
"context"
"runtime"
"sync"
"sync/atomic"
"time"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/platform"
"github.com/xtls/xray-core/common/protocol/http"
)
var timeOffset atomic.Pointer[time.Duration]
var initOnce sync.Once
func updateTimeMonitor(ctx context.Context, domain string) {
for {
select {
case <-ctx.Done():
return
case <-time.After(10 * time.Minute):
err := updateTime(domain)
if err != nil {
errors.LogError(ctx, err)
}
}
}
}
func updateTime(domain string) error {
httpClient := http.NewClient()
resp, err := httpClient.Get(domain)
if err != nil {
return errors.New("Failed to access monitor domain").Base(err)
}
timeHeader := resp.Header.Get("Date")
remoteTime, err := time.Parse(time.RFC1123, timeHeader)
if err != nil {
return errors.New("Failed to parse time from monitor domain").Base(err)
}
localTime := time.Now()
offset := remoteTime.Sub(localTime)
if offset < 2*time.Second && offset > -2*time.Second {
errors.LogWarning(context.Background(), "Time offset too small, ignoring:", offset)
return nil
}
timeOffset.Store(&offset)
return nil
}
func Now() time.Time {
initOnce.Do(func() {
timeOffset.Store(new(time.Duration))
go func() {
domain := platform.NewEnvFlag("xray.vmess.time.domain").GetValue(func() string { return "https://apple.com" })
if domain == "" {
errors.LogError(context.Background(), "vmess time domain is empty, skip time sync")
return
}
err := updateTime(domain)
if err != nil {
errors.LogError(context.Background(), err)
}
errors.LogWarning(context.Background(), "Initial time offset for vmess:", timeOffset.Load())
// only one sync should be enough, so disable periodic update for now
//go updateTimeMonitor(context.TODO(), domain)
}()
runtime.Gosched()
})
offset := timeOffset.Load()
return time.Now().Add(*offset)
}