mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-11-05 04:29:19 +02:00
XUDP protocol: Add Global ID & UoT Migration
The first UoT protocol that supports UoT Migration Thank @yuhan6665 for testing
This commit is contained in:
parent
67affe3753
commit
be23d5d3b7
|
@ -595,6 +595,8 @@ type MultiplexingConfig struct {
|
||||||
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||||
// Max number of concurrent connections that one Mux connection can handle.
|
// Max number of concurrent connections that one Mux connection can handle.
|
||||||
Concurrency uint32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"`
|
Concurrency uint32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"`
|
||||||
|
// Both(0), TCP(1), UDP(2).
|
||||||
|
Only uint32 `protobuf:"varint,3,opt,name=only,proto3" json:"only,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *MultiplexingConfig) Reset() {
|
func (x *MultiplexingConfig) Reset() {
|
||||||
|
@ -643,6 +645,13 @@ func (x *MultiplexingConfig) GetConcurrency() uint32 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *MultiplexingConfig) GetOnly() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Only
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type AllocationStrategy_AllocationStrategyConcurrency struct {
|
type AllocationStrategy_AllocationStrategyConcurrency struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -856,21 +865,22 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
||||||
0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
|
0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
|
||||||
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78,
|
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78,
|
||||||
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69,
|
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69,
|
||||||
0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x50, 0x0a, 0x12,
|
0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x64, 0x0a, 0x12,
|
||||||
0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66,
|
0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
|
0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b,
|
0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b,
|
||||||
0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x2a, 0x23,
|
0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12,
|
||||||
0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73,
|
0x0a, 0x04, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6f, 0x6e,
|
||||||
0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4c,
|
0x6c, 0x79, 0x2a, 0x23, 0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x63, 0x6f, 0x6c, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07,
|
||||||
0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26,
|
0x0a, 0x03, 0x54, 0x4c, 0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
||||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f,
|
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e,
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72,
|
0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
||||||
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70,
|
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70,
|
||||||
0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61,
|
||||||
0x6f, 0x33,
|
0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -98,4 +98,6 @@ message MultiplexingConfig {
|
||||||
bool enabled = 1;
|
bool enabled = 1;
|
||||||
// Max number of concurrent connections that one Mux connection can handle.
|
// Max number of concurrent connections that one Mux connection can handle.
|
||||||
uint32 concurrency = 2;
|
uint32 concurrency = 2;
|
||||||
|
// Both(0), TCP(1), UDP(2).
|
||||||
|
uint32 only = 3;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
||||||
return nil, newError("invalid mux concurrency: ", config.Concurrency).AtWarning()
|
return nil, newError("invalid mux concurrency: ", config.Concurrency).AtWarning()
|
||||||
}
|
}
|
||||||
h.mux = &mux.ClientManager{
|
h.mux = &mux.ClientManager{
|
||||||
Enabled: h.senderSettings.MultiplexSettings.Enabled,
|
Enabled: config.Enabled,
|
||||||
Picker: &mux.IncrementalWorkerPicker{
|
Picker: &mux.IncrementalWorkerPicker{
|
||||||
Factory: &mux.DialingWorkerFactory{
|
Factory: &mux.DialingWorkerFactory{
|
||||||
Proxy: proxyHandler,
|
Proxy: proxyHandler,
|
||||||
|
@ -122,6 +122,7 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Only: config.Only,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +137,9 @@ func (h *Handler) Tag() string {
|
||||||
|
|
||||||
// Dispatch implements proxy.Outbound.Dispatch.
|
// Dispatch implements proxy.Outbound.Dispatch.
|
||||||
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
||||||
if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) {
|
outbound := session.OutboundFromContext(ctx)
|
||||||
|
if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) &&
|
||||||
|
(h.mux.Only == 0 || (outbound != nil && h.mux.Only == uint32(outbound.Target.Network))) {
|
||||||
if err := h.mux.Dispatch(ctx, link); err != nil {
|
if err := h.mux.Dispatch(ctx, link); err != nil {
|
||||||
err := newError("failed to process mux outbound traffic").Base(err)
|
err := newError("failed to process mux outbound traffic").Base(err)
|
||||||
session.SubmitOutboundErrorToOriginator(ctx, err)
|
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/common/signal/done"
|
"github.com/xtls/xray-core/common/signal/done"
|
||||||
"github.com/xtls/xray-core/common/task"
|
"github.com/xtls/xray-core/common/task"
|
||||||
|
"github.com/xtls/xray-core/common/xudp"
|
||||||
"github.com/xtls/xray-core/proxy"
|
"github.com/xtls/xray-core/proxy"
|
||||||
"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"
|
||||||
|
@ -23,6 +24,7 @@ import (
|
||||||
type ClientManager struct {
|
type ClientManager struct {
|
||||||
Enabled bool // wheather mux is enabled from user config
|
Enabled bool // wheather mux is enabled from user config
|
||||||
Picker WorkerPicker
|
Picker WorkerPicker
|
||||||
|
Only uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ClientManager) Dispatch(ctx context.Context, link *transport.Link) error {
|
func (m *ClientManager) Dispatch(ctx context.Context, link *transport.Link) error {
|
||||||
|
@ -247,22 +249,20 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
||||||
transferType = protocol.TransferTypePacket
|
transferType = protocol.TransferTypePacket
|
||||||
}
|
}
|
||||||
s.transferType = transferType
|
s.transferType = transferType
|
||||||
writer := NewWriter(s.ID, dest, output, transferType)
|
writer := NewWriter(s.ID, dest, output, transferType, xudp.GetGlobalID(ctx))
|
||||||
defer s.Close()
|
defer s.Close(false)
|
||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
|
|
||||||
newError("dispatching request to ", dest).WriteToLog(session.ExportIDToError(ctx))
|
newError("dispatching request to ", dest).WriteToLog(session.ExportIDToError(ctx))
|
||||||
if err := writeFirstPayload(s.input, writer); err != nil {
|
if err := writeFirstPayload(s.input, writer); err != nil {
|
||||||
newError("failed to write first payload").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
newError("failed to write first payload").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
writer.hasError = true
|
writer.hasError = true
|
||||||
common.Interrupt(s.input)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := buf.Copy(s.input, writer); err != nil {
|
if err := buf.Copy(s.input, writer); err != nil {
|
||||||
newError("failed to fetch all input").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
newError("failed to fetch all input").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
writer.hasError = true
|
writer.hasError = true
|
||||||
common.Interrupt(s.input)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,15 +335,8 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||||
err := buf.Copy(rr, s.output)
|
err := buf.Copy(rr, s.output)
|
||||||
if err != nil && buf.IsWriteError(err) {
|
if err != nil && buf.IsWriteError(err) {
|
||||||
newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog()
|
newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog()
|
||||||
|
s.Close(false)
|
||||||
// Notify remote peer to close this session.
|
return buf.Copy(rr, buf.Discard)
|
||||||
closingWriter := NewResponseWriter(meta.SessionID, m.link.Writer, protocol.TransferTypeStream)
|
|
||||||
closingWriter.Close()
|
|
||||||
|
|
||||||
drainErr := buf.Copy(rr, buf.Discard)
|
|
||||||
common.Interrupt(s.input)
|
|
||||||
s.Close()
|
|
||||||
return drainErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -351,12 +344,7 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||||
|
|
||||||
func (m *ClientWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
func (m *ClientWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||||
if s, found := m.sessionManager.Get(meta.SessionID); found {
|
if s, found := m.sessionManager.Get(meta.SessionID); found {
|
||||||
if meta.Option.Has(OptionError) {
|
s.Close(false)
|
||||||
common.Interrupt(s.input)
|
|
||||||
common.Interrupt(s.output)
|
|
||||||
}
|
|
||||||
common.Interrupt(s.input)
|
|
||||||
s.Close()
|
|
||||||
}
|
}
|
||||||
if meta.Option.Has(OptionData) {
|
if meta.Option.Has(OptionData) {
|
||||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||||
|
|
|
@ -58,6 +58,7 @@ type FrameMetadata struct {
|
||||||
SessionID uint16
|
SessionID uint16
|
||||||
Option bitmask.Byte
|
Option bitmask.Byte
|
||||||
SessionStatus SessionStatus
|
SessionStatus SessionStatus
|
||||||
|
GlobalID [8]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||||
|
@ -81,6 +82,9 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||||
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if b.UDP != nil {
|
||||||
|
b.Write(f.GlobalID[:])
|
||||||
|
}
|
||||||
} else if b.UDP != nil {
|
} else if b.UDP != nil {
|
||||||
b.WriteByte(byte(TargetNetworkUDP))
|
b.WriteByte(byte(TargetNetworkUDP))
|
||||||
addrParser.WriteAddressPort(b, b.UDP.Address, b.UDP.Port)
|
addrParser.WriteAddressPort(b, b.UDP.Address, b.UDP.Port)
|
||||||
|
@ -144,5 +148,10 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f.SessionStatus == SessionStatusNew && f.Option.Has(OptionData) &&
|
||||||
|
f.Target.Network == net.Network_UDP && b.Len() >= 8 {
|
||||||
|
copy(f.GlobalID[:], b.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,13 @@ func TestReaderWriter(t *testing.T) {
|
||||||
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
|
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
|
||||||
|
|
||||||
dest := net.TCPDestination(net.DomainAddress("example.com"), 80)
|
dest := net.TCPDestination(net.DomainAddress("example.com"), 80)
|
||||||
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream)
|
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||||
|
|
||||||
dest2 := net.TCPDestination(net.LocalHostIP, 443)
|
dest2 := net.TCPDestination(net.LocalHostIP, 443)
|
||||||
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream)
|
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||||
|
|
||||||
dest3 := net.TCPDestination(net.LocalHostIPv6, 18374)
|
dest3 := net.TCPDestination(net.LocalHostIPv6, 18374)
|
||||||
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream)
|
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||||
|
|
||||||
writePayload := func(writer *Writer, payload ...byte) error {
|
writePayload := func(writer *Writer, payload ...byte) error {
|
||||||
b := buf.New()
|
b := buf.New()
|
||||||
|
|
|
@ -2,6 +2,7 @@ package mux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
@ -11,6 +12,7 @@ import (
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/xudp"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
|
@ -99,7 +101,7 @@ func handle(ctx context.Context, s *Session, output buf.Writer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Close()
|
writer.Close()
|
||||||
s.Close()
|
s.Close(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *ServerWorker) ActiveConnections() uint32 {
|
func (w *ServerWorker) ActiveConnections() uint32 {
|
||||||
|
@ -131,6 +133,81 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||||
}
|
}
|
||||||
ctx = log.ContextWithAccessMessage(ctx, msg)
|
ctx = log.ContextWithAccessMessage(ctx, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if meta.GlobalID != [8]byte{} {
|
||||||
|
mb, err := NewPacketReader(reader, &meta.Target).ReadMultiBuffer()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
XUDPManager.Lock()
|
||||||
|
x := XUDPManager.Map[meta.GlobalID]
|
||||||
|
if x == nil {
|
||||||
|
x = &XUDP{GlobalID: meta.GlobalID}
|
||||||
|
XUDPManager.Map[meta.GlobalID] = x
|
||||||
|
XUDPManager.Unlock()
|
||||||
|
} else {
|
||||||
|
if x.Status == Initializing { // nearly impossible
|
||||||
|
XUDPManager.Unlock()
|
||||||
|
if xudp.Show {
|
||||||
|
fmt.Printf("XUDP hit: %v err: conflict\n", meta.GlobalID)
|
||||||
|
}
|
||||||
|
// It's not a good idea to return an err here, so just let client wait.
|
||||||
|
// Client will receive an End frame after sending a Keep frame.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
x.Status = Initializing
|
||||||
|
XUDPManager.Unlock()
|
||||||
|
x.Mux.Close(false) // detach from previous Mux
|
||||||
|
b := buf.New()
|
||||||
|
b.Write(mb[0].Bytes())
|
||||||
|
b.UDP = mb[0].UDP
|
||||||
|
if err = x.Mux.output.WriteMultiBuffer(mb); err != nil {
|
||||||
|
x.Interrupt()
|
||||||
|
mb = buf.MultiBuffer{b}
|
||||||
|
} else {
|
||||||
|
b.Release()
|
||||||
|
mb = nil
|
||||||
|
}
|
||||||
|
if xudp.Show {
|
||||||
|
fmt.Printf("XUDP hit: %v err: %v\n", meta.GlobalID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mb != nil {
|
||||||
|
ctx = session.ContextWithTimeoutOnly(ctx, true)
|
||||||
|
// Actually, it won't return an error in Xray-core's implementations.
|
||||||
|
link, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
||||||
|
if err != nil {
|
||||||
|
err = newError("failed to dispatch request to ", meta.Target).Base(err)
|
||||||
|
if xudp.Show {
|
||||||
|
fmt.Printf("XUDP new: %v err: %v\n", meta.GlobalID, err)
|
||||||
|
}
|
||||||
|
return err // it will break the whole Mux connection
|
||||||
|
}
|
||||||
|
link.Writer.WriteMultiBuffer(mb) // it's meaningless to test a new pipe
|
||||||
|
x.Mux = &Session{
|
||||||
|
input: link.Reader,
|
||||||
|
output: link.Writer,
|
||||||
|
}
|
||||||
|
if xudp.Show {
|
||||||
|
fmt.Printf("XUDP new: %v err: %v\n", meta.GlobalID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x.Mux = &Session{
|
||||||
|
input: x.Mux.input,
|
||||||
|
output: x.Mux.output,
|
||||||
|
parent: w.sessionManager,
|
||||||
|
ID: meta.SessionID,
|
||||||
|
transferType: protocol.TransferTypePacket,
|
||||||
|
XUDP: x,
|
||||||
|
}
|
||||||
|
go handle(ctx, x.Mux, w.link.Writer)
|
||||||
|
x.Status = Active
|
||||||
|
if !w.sessionManager.Add(x.Mux) {
|
||||||
|
x.Mux.Close(false)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
link, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
link, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if meta.Option.Has(OptionData) {
|
if meta.Option.Has(OptionData) {
|
||||||
|
@ -157,8 +234,7 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||||
rr := s.NewReader(reader, &meta.Target)
|
rr := s.NewReader(reader, &meta.Target)
|
||||||
if err := buf.Copy(rr, s.output); err != nil {
|
if err := buf.Copy(rr, s.output); err != nil {
|
||||||
buf.Copy(rr, buf.Discard)
|
buf.Copy(rr, buf.Discard)
|
||||||
common.Interrupt(s.input)
|
return s.Close(false)
|
||||||
return s.Close()
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -182,15 +258,8 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||||
|
|
||||||
if err != nil && buf.IsWriteError(err) {
|
if err != nil && buf.IsWriteError(err) {
|
||||||
newError("failed to write to downstream writer. closing session ", s.ID).Base(err).WriteToLog()
|
newError("failed to write to downstream writer. closing session ", s.ID).Base(err).WriteToLog()
|
||||||
|
s.Close(false)
|
||||||
// Notify remote peer to close this session.
|
return buf.Copy(rr, buf.Discard)
|
||||||
closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream)
|
|
||||||
closingWriter.Close()
|
|
||||||
|
|
||||||
drainErr := buf.Copy(rr, buf.Discard)
|
|
||||||
common.Interrupt(s.input)
|
|
||||||
s.Close()
|
|
||||||
return drainErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -198,12 +267,7 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||||
|
|
||||||
func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||||
if s, found := w.sessionManager.Get(meta.SessionID); found {
|
if s, found := w.sessionManager.Get(meta.SessionID); found {
|
||||||
if meta.Option.Has(OptionError) {
|
s.Close(false)
|
||||||
common.Interrupt(s.input)
|
|
||||||
common.Interrupt(s.output)
|
|
||||||
}
|
|
||||||
common.Interrupt(s.input)
|
|
||||||
s.Close()
|
|
||||||
}
|
}
|
||||||
if meta.Option.Has(OptionData) {
|
if meta.Option.Has(OptionData) {
|
||||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
package mux
|
package mux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
|
"github.com/xtls/xray-core/common/xudp"
|
||||||
|
"github.com/xtls/xray-core/transport/pipe"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SessionManager struct {
|
type SessionManager struct {
|
||||||
|
@ -61,21 +67,25 @@ func (m *SessionManager) Allocate() *Session {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SessionManager) Add(s *Session) {
|
func (m *SessionManager) Add(s *Session) bool {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
if m.closed {
|
if m.closed {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
m.count++
|
m.count++
|
||||||
m.sessions[s.ID] = s
|
m.sessions[s.ID] = s
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SessionManager) Remove(id uint16) {
|
func (m *SessionManager) Remove(locked bool, id uint16) {
|
||||||
|
if !locked {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
}
|
||||||
|
locked = true
|
||||||
|
|
||||||
if m.closed {
|
if m.closed {
|
||||||
return
|
return
|
||||||
|
@ -83,9 +93,11 @@ func (m *SessionManager) Remove(id uint16) {
|
||||||
|
|
||||||
delete(m.sessions, id)
|
delete(m.sessions, id)
|
||||||
|
|
||||||
|
/*
|
||||||
if len(m.sessions) == 0 {
|
if len(m.sessions) == 0 {
|
||||||
m.sessions = make(map[uint16]*Session, 16)
|
m.sessions = make(map[uint16]*Session, 16)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SessionManager) Get(id uint16) (*Session, bool) {
|
func (m *SessionManager) Get(id uint16) (*Session, bool) {
|
||||||
|
@ -127,8 +139,7 @@ func (m *SessionManager) Close() error {
|
||||||
m.closed = true
|
m.closed = true
|
||||||
|
|
||||||
for _, s := range m.sessions {
|
for _, s := range m.sessions {
|
||||||
common.Close(s.input)
|
s.Close(true)
|
||||||
common.Close(s.output)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.sessions = nil
|
m.sessions = nil
|
||||||
|
@ -142,13 +153,42 @@ type Session struct {
|
||||||
parent *SessionManager
|
parent *SessionManager
|
||||||
ID uint16
|
ID uint16
|
||||||
transferType protocol.TransferType
|
transferType protocol.TransferType
|
||||||
|
closed bool
|
||||||
|
XUDP *XUDP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes all resources associated with this session.
|
// Close closes all resources associated with this session.
|
||||||
func (s *Session) Close() error {
|
func (s *Session) Close(locked bool) error {
|
||||||
|
if !locked {
|
||||||
|
s.parent.Lock()
|
||||||
|
defer s.parent.Unlock()
|
||||||
|
}
|
||||||
|
locked = true
|
||||||
|
if s.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.closed = true
|
||||||
|
if s.XUDP == nil {
|
||||||
|
common.Interrupt(s.input)
|
||||||
common.Close(s.output)
|
common.Close(s.output)
|
||||||
common.Close(s.input)
|
} else {
|
||||||
s.parent.Remove(s.ID)
|
// Stop existing handle(), then trigger writer.Close().
|
||||||
|
// Note that s.output may be dispatcher.SizeStatWriter.
|
||||||
|
s.input.(*pipe.Reader).ReturnAnError(io.EOF)
|
||||||
|
runtime.Gosched()
|
||||||
|
// If the error set by ReturnAnError still exists, clear it.
|
||||||
|
s.input.(*pipe.Reader).Recover()
|
||||||
|
XUDPManager.Lock()
|
||||||
|
if s.XUDP.Status == Active {
|
||||||
|
s.XUDP.Expire = time.Now().Add(time.Minute)
|
||||||
|
s.XUDP.Status = Expiring
|
||||||
|
if xudp.Show {
|
||||||
|
fmt.Printf("XUDP put: %v\n", s.XUDP.GlobalID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XUDPManager.Unlock()
|
||||||
|
}
|
||||||
|
s.parent.Remove(locked, s.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,3 +199,47 @@ func (s *Session) NewReader(reader *buf.BufferedReader, dest *net.Destination) b
|
||||||
}
|
}
|
||||||
return NewPacketReader(reader, dest)
|
return NewPacketReader(reader, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Initializing = 0
|
||||||
|
Active = 1
|
||||||
|
Expiring = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type XUDP struct {
|
||||||
|
GlobalID [8]byte
|
||||||
|
Status uint64
|
||||||
|
Expire time.Time
|
||||||
|
Mux *Session
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XUDP) Interrupt() {
|
||||||
|
common.Interrupt(x.Mux.input)
|
||||||
|
common.Close(x.Mux.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
var XUDPManager struct {
|
||||||
|
sync.Mutex
|
||||||
|
Map map[[8]byte]*XUDP
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
XUDPManager.Map = make(map[[8]byte]*XUDP)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Minute)
|
||||||
|
now := time.Now()
|
||||||
|
XUDPManager.Lock()
|
||||||
|
for id, x := range XUDPManager.Map {
|
||||||
|
if x.Status == Expiring && now.After(x.Expire) {
|
||||||
|
x.Interrupt()
|
||||||
|
delete(XUDPManager.Map, id)
|
||||||
|
if xudp.Show {
|
||||||
|
fmt.Printf("XUDP del: %v\n", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XUDPManager.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ func TestSessionManagerClose(t *testing.T) {
|
||||||
if m.CloseIfNoSession() {
|
if m.CloseIfNoSession() {
|
||||||
t.Error("able to close")
|
t.Error("able to close")
|
||||||
}
|
}
|
||||||
m.Remove(s.ID)
|
m.Remove(false, s.ID)
|
||||||
if !m.CloseIfNoSession() {
|
if !m.CloseIfNoSession() {
|
||||||
t.Error("not able to close")
|
t.Error("not able to close")
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,17 @@ type Writer struct {
|
||||||
followup bool
|
followup bool
|
||||||
hasError bool
|
hasError bool
|
||||||
transferType protocol.TransferType
|
transferType protocol.TransferType
|
||||||
|
globalID [8]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType) *Writer {
|
func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType, globalID [8]byte) *Writer {
|
||||||
return &Writer{
|
return &Writer{
|
||||||
id: id,
|
id: id,
|
||||||
dest: dest,
|
dest: dest,
|
||||||
writer: writer,
|
writer: writer,
|
||||||
followup: false,
|
followup: false,
|
||||||
transferType: transferType,
|
transferType: transferType,
|
||||||
|
globalID: globalID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +42,7 @@ func (w *Writer) getNextFrameMeta() FrameMetadata {
|
||||||
meta := FrameMetadata{
|
meta := FrameMetadata{
|
||||||
SessionID: w.id,
|
SessionID: w.id,
|
||||||
Target: w.dest,
|
Target: w.dest,
|
||||||
|
GlobalID: w.globalID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.followup {
|
if w.followup {
|
||||||
|
|
|
@ -2,10 +2,14 @@ package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:linkname IndependentCancelCtx context.newCancelCtx
|
||||||
|
func IndependentCancelCtx(parent context.Context) context.Context
|
||||||
|
|
||||||
type sessionKey int
|
type sessionKey int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -17,6 +21,7 @@ const (
|
||||||
sockoptSessionKey
|
sockoptSessionKey
|
||||||
trackedConnectionErrorKey
|
trackedConnectionErrorKey
|
||||||
dispatcherKey
|
dispatcherKey
|
||||||
|
timeoutOnlyKey
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContextWithID returns a new context with the given ID.
|
// ContextWithID returns a new context with the given ID.
|
||||||
|
@ -131,3 +136,14 @@ func DispatcherFromContext(ctx context.Context) routing.Dispatcher {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ContextWithTimeoutOnly(ctx context.Context, only bool) context.Context {
|
||||||
|
return context.WithValue(ctx, timeoutOnlyKey, only)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeoutOnlyFromContext(ctx context.Context) bool {
|
||||||
|
if val, ok := ctx.Value(timeoutOnlyKey).(bool); ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ type Inbound struct {
|
||||||
Gateway net.Destination
|
Gateway net.Destination
|
||||||
// Tag of the inbound proxy that handles the connection.
|
// Tag of the inbound proxy that handles the connection.
|
||||||
Tag string
|
Tag string
|
||||||
|
// Name of the inbound proxy that handles the connection.
|
||||||
|
Name string
|
||||||
// User is the user that authencates for the inbound. May be nil if the protocol allows anounymous traffic.
|
// User is the user that authencates for the inbound. May be nil if the protocol allows anounymous traffic.
|
||||||
User *protocol.MemoryUser
|
User *protocol.MemoryUser
|
||||||
// Conn is actually internet.Connection. May be nil.
|
// Conn is actually internet.Connection. May be nil.
|
||||||
|
|
|
@ -38,6 +38,12 @@ func Run(ctx context.Context, tasks ...func() error) error {
|
||||||
}(task)
|
}(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if altctx := ctx.Value("altctx"); altctx != nil {
|
||||||
|
ctx = altctx.(context.Context)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
select {
|
select {
|
||||||
case err := <-done:
|
case err := <-done:
|
||||||
|
@ -48,5 +54,11 @@ func Run(ctx context.Context, tasks ...func() error) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if cancel := ctx.Value("cancel"); cancel != nil {
|
||||||
|
cancel.(context.CancelFunc)()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,76 @@
|
||||||
package xudp
|
package xudp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"lukechampine.com/blake3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var addrParser = protocol.NewAddressParser(
|
var AddrParser = protocol.NewAddressParser(
|
||||||
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4),
|
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4),
|
||||||
protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain),
|
protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain),
|
||||||
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6),
|
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6),
|
||||||
protocol.PortThenAddress(),
|
protocol.PortThenAddress(),
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPacketWriter(writer buf.Writer, dest net.Destination) *PacketWriter {
|
var (
|
||||||
|
Show bool
|
||||||
|
BaseKey [32]byte
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EnvShow = "XRAY_XUDP_SHOW"
|
||||||
|
EnvBaseKey = "XRAY_XUDP_BASEKEY"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if strings.ToLower(os.Getenv(EnvShow)) == "true" {
|
||||||
|
Show = true
|
||||||
|
}
|
||||||
|
if raw := os.Getenv(EnvBaseKey); raw != "" {
|
||||||
|
if key, _ := base64.RawURLEncoding.DecodeString(raw); len(key) == len(BaseKey) {
|
||||||
|
copy(BaseKey[:], key)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
panic(EnvBaseKey + ": invalid value: " + raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rand.Read(BaseKey[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGlobalID(ctx context.Context) (globalID [8]byte) {
|
||||||
|
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.Network == net.Network_UDP &&
|
||||||
|
(inbound.Name == "dokodemo-door" || inbound.Name == "socks" || inbound.Name == "shadowsocks") {
|
||||||
|
h := blake3.New(8, BaseKey[:])
|
||||||
|
h.Write([]byte(inbound.Source.String()))
|
||||||
|
copy(globalID[:], h.Sum(nil))
|
||||||
|
fmt.Printf("XUDP inbound.Source.String(): %v\tglobalID: %v\n", inbound.Source.String(), globalID)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPacketWriter(writer buf.Writer, dest net.Destination, globalID [8]byte) *PacketWriter {
|
||||||
return &PacketWriter{
|
return &PacketWriter{
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
Dest: dest,
|
Dest: dest,
|
||||||
|
GlobalID: globalID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PacketWriter struct {
|
type PacketWriter struct {
|
||||||
Writer buf.Writer
|
Writer buf.Writer
|
||||||
Dest net.Destination
|
Dest net.Destination
|
||||||
|
GlobalID [8]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
|
@ -42,14 +88,17 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
eb.WriteByte(1) // New
|
eb.WriteByte(1) // New
|
||||||
eb.WriteByte(1) // Opt
|
eb.WriteByte(1) // Opt
|
||||||
eb.WriteByte(2) // UDP
|
eb.WriteByte(2) // UDP
|
||||||
addrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port)
|
AddrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port)
|
||||||
|
if b.UDP != nil { // make sure it's user's proxy request
|
||||||
|
eb.Write(w.GlobalID[:])
|
||||||
|
}
|
||||||
w.Dest.Network = net.Network_Unknown
|
w.Dest.Network = net.Network_Unknown
|
||||||
} else {
|
} else {
|
||||||
eb.WriteByte(2) // Keep
|
eb.WriteByte(2) // Keep
|
||||||
eb.WriteByte(1)
|
eb.WriteByte(1)
|
||||||
if b.UDP != nil {
|
if b.UDP != nil {
|
||||||
eb.WriteByte(2)
|
eb.WriteByte(2)
|
||||||
addrParser.WriteAddressPort(eb, b.UDP.Address, b.UDP.Port)
|
AddrParser.WriteAddressPort(eb, b.UDP.Address, b.UDP.Port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
l := eb.Len() - 2
|
l := eb.Len() - 2
|
||||||
|
@ -98,7 +147,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
case 2:
|
case 2:
|
||||||
if l != 4 {
|
if l != 4 {
|
||||||
b.Advance(5)
|
b.Advance(5)
|
||||||
addr, port, err := addrParser.ReadAddressPort(nil, b)
|
addr, port, err := AddrParser.ReadAddressPort(nil, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Release()
|
b.Release()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -29,6 +29,7 @@ require (
|
||||||
google.golang.org/protobuf v1.30.0
|
google.golang.org/protobuf v1.30.0
|
||||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||||
h12.io/socks v1.0.3
|
h12.io/socks v1.0.3
|
||||||
|
lukechampine.com/blake3 v1.1.7
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -55,5 +56,4 @@ require (
|
||||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
|
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
lukechampine.com/blake3 v1.1.7 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/xtls/xray-core/app/dispatcher"
|
"github.com/xtls/xray-core/app/dispatcher"
|
||||||
"github.com/xtls/xray-core/app/proxyman"
|
"github.com/xtls/xray-core/app/proxyman"
|
||||||
"github.com/xtls/xray-core/app/stats"
|
"github.com/xtls/xray-core/app/stats"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
core "github.com/xtls/xray-core/core"
|
core "github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
@ -109,6 +110,7 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
|
||||||
type MuxConfig struct {
|
type MuxConfig struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
Concurrency int16 `json:"concurrency"`
|
Concurrency int16 `json:"concurrency"`
|
||||||
|
Only string `json:"only"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build creates MultiplexingConfig, Concurrency < 0 completely disables mux.
|
// Build creates MultiplexingConfig, Concurrency < 0 completely disables mux.
|
||||||
|
@ -116,16 +118,23 @@ func (m *MuxConfig) Build() *proxyman.MultiplexingConfig {
|
||||||
if m.Concurrency < 0 {
|
if m.Concurrency < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if m.Concurrency == 0 {
|
||||||
var con uint32 = 8
|
m.Concurrency = 8
|
||||||
if m.Concurrency > 0 {
|
|
||||||
con = uint32(m.Concurrency)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &proxyman.MultiplexingConfig{
|
config := &proxyman.MultiplexingConfig{
|
||||||
Enabled: m.Enabled,
|
Enabled: m.Enabled,
|
||||||
Concurrency: con,
|
Concurrency: uint32(m.Concurrency),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(m.Only) {
|
||||||
|
case "tcp":
|
||||||
|
config.Only = uint32(net.Network_TCP)
|
||||||
|
case "udp":
|
||||||
|
config.Only = uint32(net.Network_UDP)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
type InboundDetourAllocationConfig struct {
|
type InboundDetourAllocationConfig struct {
|
||||||
|
|
|
@ -148,6 +148,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
ctx, _ = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, h.timeout)
|
timer := signal.CancelAfterInactivity(ctx, cancel, h.timeout)
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
|
||||||
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
if inbound != nil {
|
if inbound != nil {
|
||||||
|
inbound.Name = "dokodemo-door"
|
||||||
inbound.User = &protocol.MemoryUser{
|
inbound.User = &protocol.MemoryUser{
|
||||||
Level: d.config.UserLevel,
|
Level: d.config.UserLevel,
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,9 +149,20 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
var newCtx context.Context
|
||||||
|
var newCancel context.CancelFunc
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
newCtx, newCancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
plcy := h.policy()
|
plcy := h.policy()
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle)
|
timer := signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
cancel()
|
||||||
|
if newCancel != nil {
|
||||||
|
newCancel()
|
||||||
|
}
|
||||||
|
}, plcy.Timeouts.ConnectionIdle)
|
||||||
|
|
||||||
requestDone := func() error {
|
requestDone := func() error {
|
||||||
defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
|
defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
|
||||||
|
@ -186,6 +197,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newCtx != nil {
|
||||||
|
ctx = newCtx
|
||||||
|
}
|
||||||
|
|
||||||
if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil {
|
if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil {
|
||||||
return newError("connection ends").Base(err)
|
return newError("connection ends").Base(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,8 +128,19 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||||
p = c.policyManager.ForLevel(user.Level)
|
p = c.policyManager.ForLevel(user.Level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newCtx context.Context
|
||||||
|
var newCancel context.CancelFunc
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
newCtx, newCancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle)
|
timer := signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
cancel()
|
||||||
|
if newCancel != nil {
|
||||||
|
newCancel()
|
||||||
|
}
|
||||||
|
}, p.Timeouts.ConnectionIdle)
|
||||||
|
|
||||||
requestFunc := func() error {
|
requestFunc := func() error {
|
||||||
defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
|
defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
|
||||||
|
@ -140,6 +151,10 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||||
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
|
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newCtx != nil {
|
||||||
|
ctx = newCtx
|
||||||
|
}
|
||||||
|
|
||||||
responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
|
responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
|
||||||
if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
|
if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
|
||||||
return newError("connection ends").Base(err)
|
return newError("connection ends").Base(err)
|
||||||
|
|
|
@ -85,6 +85,7 @@ type readerOnly struct {
|
||||||
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
if inbound != nil {
|
if inbound != nil {
|
||||||
|
inbound.Name = "http"
|
||||||
inbound.User = &protocol.MemoryUser{
|
inbound.User = &protocol.MemoryUser{
|
||||||
Level: s.config.UserLevel,
|
Level: s.config.UserLevel,
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,9 +96,24 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||||
}
|
}
|
||||||
request.User = user
|
request.User = user
|
||||||
|
|
||||||
|
var newCtx context.Context
|
||||||
|
var newCancel context.CancelFunc
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
newCtx, newCancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
sessionPolicy := c.policyManager.ForLevel(user.Level)
|
sessionPolicy := c.policyManager.ForLevel(user.Level)
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
timer := signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
cancel()
|
||||||
|
if newCancel != nil {
|
||||||
|
newCancel()
|
||||||
|
}
|
||||||
|
}, sessionPolicy.Timeouts.ConnectionIdle)
|
||||||
|
|
||||||
|
if newCtx != nil {
|
||||||
|
ctx = newCtx
|
||||||
|
}
|
||||||
|
|
||||||
if request.Command == protocol.RequestCommandTCP {
|
if request.Command == protocol.RequestCommandTCP {
|
||||||
requestDone := func() error {
|
requestDone := func() error {
|
||||||
|
|
|
@ -113,6 +113,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
|
||||||
if inbound == nil {
|
if inbound == nil {
|
||||||
panic("no inbound metadata")
|
panic("no inbound metadata")
|
||||||
}
|
}
|
||||||
|
inbound.Name = "shadowsocks"
|
||||||
|
|
||||||
var dest *net.Destination
|
var dest *net.Destination
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package shadowsocks_2022
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/sagernet/sing-shadowsocks"
|
shadowsocks "github.com/sagernet/sing-shadowsocks"
|
||||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||||
C "github.com/sagernet/sing/common"
|
C "github.com/sagernet/sing/common"
|
||||||
B "github.com/sagernet/sing/common/buf"
|
B "github.com/sagernet/sing/common/buf"
|
||||||
|
@ -64,6 +64,7 @@ func (i *Inbound) Network() []net.Network {
|
||||||
|
|
||||||
func (i *Inbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
func (i *Inbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
|
inbound.Name = "shadowsocks-2022"
|
||||||
|
|
||||||
var metadata M.Metadata
|
var metadata M.Metadata
|
||||||
if inbound.Source.IsValid() {
|
if inbound.Source.IsValid() {
|
||||||
|
|
|
@ -153,6 +153,7 @@ func (i *MultiUserInbound) Network() []net.Network {
|
||||||
|
|
||||||
func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
|
inbound.Name = "shadowsocks-2022-multi"
|
||||||
|
|
||||||
var metadata M.Metadata
|
var metadata M.Metadata
|
||||||
if inbound.Source.IsValid() {
|
if inbound.Source.IsValid() {
|
||||||
|
|
|
@ -85,6 +85,7 @@ func (i *RelayInbound) Network() []net.Network {
|
||||||
|
|
||||||
func (i *RelayInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
func (i *RelayInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
|
inbound.Name = "shadowsocks-2022-relay"
|
||||||
|
|
||||||
var metadata M.Metadata
|
var metadata M.Metadata
|
||||||
if inbound.Source.IsValid() {
|
if inbound.Source.IsValid() {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-shadowsocks"
|
shadowsocks "github.com/sagernet/sing-shadowsocks"
|
||||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||||
C "github.com/sagernet/sing/common"
|
C "github.com/sagernet/sing/common"
|
||||||
B "github.com/sagernet/sing/common/buf"
|
B "github.com/sagernet/sing/common/buf"
|
||||||
|
@ -88,6 +88,10 @@ func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer int
|
||||||
return newError("failed to connect to server").Base(err)
|
return newError("failed to connect to server").Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
ctx, _ = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
if network == net.Network_TCP {
|
if network == net.Network_TCP {
|
||||||
serverConn := o.method.DialEarlyConn(connection, toSocksaddr(destination))
|
serverConn := o.method.DialEarlyConn(connection, toSocksaddr(destination))
|
||||||
var handshake bool
|
var handshake bool
|
||||||
|
|
|
@ -151,8 +151,19 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||||
newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newCtx context.Context
|
||||||
|
var newCancel context.CancelFunc
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
newCtx, newCancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle)
|
timer := signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
cancel()
|
||||||
|
if newCancel != nil {
|
||||||
|
newCancel()
|
||||||
|
}
|
||||||
|
}, p.Timeouts.ConnectionIdle)
|
||||||
|
|
||||||
var requestFunc func() error
|
var requestFunc func() error
|
||||||
var responseFunc func() error
|
var responseFunc func() error
|
||||||
|
@ -183,6 +194,10 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newCtx != nil {
|
||||||
|
ctx = newCtx
|
||||||
|
}
|
||||||
|
|
||||||
responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
|
responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
|
||||||
if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
|
if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
|
||||||
return newError("connection ends").Base(err)
|
return newError("connection ends").Base(err)
|
||||||
|
|
|
@ -64,6 +64,7 @@ func (s *Server) Network() []net.Network {
|
||||||
// Process implements proxy.Inbound.
|
// Process implements proxy.Inbound.
|
||||||
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
|
inbound.Name = "socks"
|
||||||
inbound.User = &protocol.MemoryUser{
|
inbound.User = &protocol.MemoryUser{
|
||||||
Level: s.config.UserLevel,
|
Level: s.config.UserLevel,
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,9 +93,20 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||||
Flow: account.Flow,
|
Flow: account.Flow,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newCtx context.Context
|
||||||
|
var newCancel context.CancelFunc
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
newCtx, newCancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
sessionPolicy := c.policyManager.ForLevel(user.Level)
|
sessionPolicy := c.policyManager.ForLevel(user.Level)
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
timer := signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
cancel()
|
||||||
|
if newCancel != nil {
|
||||||
|
newCancel()
|
||||||
|
}
|
||||||
|
}, sessionPolicy.Timeouts.ConnectionIdle)
|
||||||
|
|
||||||
postRequest := func() error {
|
postRequest := func() error {
|
||||||
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
||||||
|
@ -149,6 +160,10 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||||
return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
|
return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newCtx != nil {
|
||||||
|
ctx = newCtx
|
||||||
|
}
|
||||||
|
|
||||||
responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer))
|
responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer))
|
||||||
if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil {
|
if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil {
|
||||||
return newError("connection ends").Base(err)
|
return newError("connection ends").Base(err)
|
||||||
|
|
|
@ -217,6 +217,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
|
||||||
if inbound == nil {
|
if inbound == nil {
|
||||||
panic("no inbound metadata")
|
panic("no inbound metadata")
|
||||||
}
|
}
|
||||||
|
inbound.Name = "trojan"
|
||||||
inbound.User = user
|
inbound.User = user
|
||||||
sessionPolicy = s.policyManager.ForLevel(user.Level)
|
sessionPolicy = s.policyManager.ForLevel(user.Level)
|
||||||
|
|
||||||
|
|
|
@ -438,6 +438,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||||
if inbound == nil {
|
if inbound == nil {
|
||||||
panic("no inbound metadata")
|
panic("no inbound metadata")
|
||||||
}
|
}
|
||||||
|
inbound.Name = "vless"
|
||||||
inbound.User = request.User
|
inbound.User = request.User
|
||||||
|
|
||||||
account := request.User.Account.(*vless.MemoryAccount)
|
account := request.User.Account.(*vless.MemoryAccount)
|
||||||
|
|
|
@ -170,9 +170,20 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newCtx context.Context
|
||||||
|
var newCancel context.CancelFunc
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
newCtx, newCancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
sessionPolicy := h.policyManager.ForLevel(request.User.Level)
|
sessionPolicy := h.policyManager.ForLevel(request.User.Level)
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
timer := signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
cancel()
|
||||||
|
if newCancel != nil {
|
||||||
|
newCancel()
|
||||||
|
}
|
||||||
|
}, sessionPolicy.Timeouts.ConnectionIdle)
|
||||||
|
|
||||||
clientReader := link.Reader // .(*pipe.Reader)
|
clientReader := link.Reader // .(*pipe.Reader)
|
||||||
clientWriter := link.Writer // .(*pipe.Writer)
|
clientWriter := link.Writer // .(*pipe.Writer)
|
||||||
|
@ -200,7 +211,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)
|
serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons)
|
||||||
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
||||||
serverWriter = xudp.NewPacketWriter(serverWriter, target)
|
serverWriter = xudp.NewPacketWriter(serverWriter, target, xudp.GetGlobalID(ctx))
|
||||||
}
|
}
|
||||||
userUUID := account.ID.Bytes()
|
userUUID := account.ID.Bytes()
|
||||||
timeoutReader, ok := clientReader.(buf.TimeoutReader)
|
timeoutReader, ok := clientReader.(buf.TimeoutReader)
|
||||||
|
@ -300,6 +311,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newCtx != nil {
|
||||||
|
ctx = newCtx
|
||||||
|
}
|
||||||
|
|
||||||
if err := task.Run(ctx, postRequest, task.OnSuccess(getResponse, task.Close(clientWriter))); err != nil {
|
if err := task.Run(ctx, postRequest, task.OnSuccess(getResponse, task.Close(clientWriter))); err != nil {
|
||||||
return newError("connection ends").Base(err).AtInfo()
|
return newError("connection ends").Base(err).AtInfo()
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,6 +287,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||||
if inbound == nil {
|
if inbound == nil {
|
||||||
panic("no inbound metadata")
|
panic("no inbound metadata")
|
||||||
}
|
}
|
||||||
|
inbound.Name = "vmess"
|
||||||
inbound.User = request.User
|
inbound.User = request.User
|
||||||
|
|
||||||
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
|
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
|
||||||
|
|
|
@ -138,11 +138,22 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
|
|
||||||
behaviorSeed := crc64.Checksum(hashkdf.Sum(nil), crc64.MakeTable(crc64.ISO))
|
behaviorSeed := crc64.Checksum(hashkdf.Sum(nil), crc64.MakeTable(crc64.ISO))
|
||||||
|
|
||||||
|
var newCtx context.Context
|
||||||
|
var newCancel context.CancelFunc
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
newCtx, newCancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
session := encoding.NewClientSession(ctx, isAEAD, protocol.DefaultIDHash, int64(behaviorSeed))
|
session := encoding.NewClientSession(ctx, isAEAD, protocol.DefaultIDHash, int64(behaviorSeed))
|
||||||
sessionPolicy := h.policyManager.ForLevel(request.User.Level)
|
sessionPolicy := h.policyManager.ForLevel(request.User.Level)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
timer := signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
cancel()
|
||||||
|
if newCancel != nil {
|
||||||
|
newCancel()
|
||||||
|
}
|
||||||
|
}, sessionPolicy.Timeouts.ConnectionIdle)
|
||||||
|
|
||||||
if request.Command == protocol.RequestCommandUDP && h.cone && request.Port != 53 && request.Port != 443 {
|
if request.Command == protocol.RequestCommandUDP && h.cone && request.Port != 53 && request.Port != 443 {
|
||||||
request.Command = protocol.RequestCommandMux
|
request.Command = protocol.RequestCommandMux
|
||||||
|
@ -164,7 +175,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
}
|
}
|
||||||
bodyWriter2 := bodyWriter
|
bodyWriter2 := bodyWriter
|
||||||
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
||||||
bodyWriter = xudp.NewPacketWriter(bodyWriter, target)
|
bodyWriter = xudp.NewPacketWriter(bodyWriter, target, xudp.GetGlobalID(ctx))
|
||||||
}
|
}
|
||||||
if err := buf.CopyOnceTimeout(input, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
|
if err := buf.CopyOnceTimeout(input, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
|
||||||
return newError("failed to write first payload").Base(err)
|
return newError("failed to write first payload").Base(err)
|
||||||
|
@ -208,6 +219,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
return buf.Copy(bodyReader, output, buf.UpdateActivity(timer))
|
return buf.Copy(bodyReader, output, buf.UpdateActivity(timer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newCtx != nil {
|
||||||
|
ctx = newCtx
|
||||||
|
}
|
||||||
|
|
||||||
responseDonePost := task.OnSuccess(responseDone, task.Close(output))
|
responseDonePost := task.OnSuccess(responseDone, task.Close(output))
|
||||||
if err := task.Run(ctx, requestDone, responseDonePost); err != nil {
|
if err := task.Run(ctx, requestDone, responseDonePost); err != nil {
|
||||||
return newError("connection ends").Base(err)
|
return newError("connection ends").Base(err)
|
||||||
|
|
|
@ -127,10 +127,21 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
addr = net.IPAddress(ips[0])
|
addr = net.IPAddress(ips[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newCtx context.Context
|
||||||
|
var newCancel context.CancelFunc
|
||||||
|
if session.TimeoutOnlyFromContext(ctx) {
|
||||||
|
newCtx, newCancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
p := h.policyManager.ForLevel(0)
|
p := h.policyManager.ForLevel(0)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle)
|
timer := signal.CancelAfterInactivity(ctx, func() {
|
||||||
|
cancel()
|
||||||
|
if newCancel != nil {
|
||||||
|
newCancel()
|
||||||
|
}
|
||||||
|
}, p.Timeouts.ConnectionIdle)
|
||||||
addrPort := netip.AddrPortFrom(toNetIpAddr(addr), destination.Port.Value())
|
addrPort := netip.AddrPortFrom(toNetIpAddr(addr), destination.Port.Value())
|
||||||
|
|
||||||
var requestFunc func() error
|
var requestFunc func() error
|
||||||
|
@ -166,6 +177,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newCtx != nil {
|
||||||
|
ctx = newCtx
|
||||||
|
}
|
||||||
|
|
||||||
responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
|
responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
|
||||||
if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
|
if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
|
||||||
return newError("connection ends").Base(err)
|
return newError("connection ends").Base(err)
|
||||||
|
|
|
@ -37,6 +37,7 @@ type pipe struct {
|
||||||
readSignal *signal.Notifier
|
readSignal *signal.Notifier
|
||||||
writeSignal *signal.Notifier
|
writeSignal *signal.Notifier
|
||||||
done *done.Instance
|
done *done.Instance
|
||||||
|
errChan chan error
|
||||||
option pipeOption
|
option pipeOption
|
||||||
state state
|
state state
|
||||||
}
|
}
|
||||||
|
@ -92,6 +93,8 @@ func (p *pipe) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
select {
|
select {
|
||||||
case <-p.readSignal.Wait():
|
case <-p.readSignal.Wait():
|
||||||
case <-p.done.Wait():
|
case <-p.done.Wait():
|
||||||
|
case err = <-p.errChan:
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ func New(opts ...Option) (*Reader, *Writer) {
|
||||||
readSignal: signal.NewNotifier(),
|
readSignal: signal.NewNotifier(),
|
||||||
writeSignal: signal.NewNotifier(),
|
writeSignal: signal.NewNotifier(),
|
||||||
done: done.New(),
|
done: done.New(),
|
||||||
|
errChan: make(chan error, 1),
|
||||||
option: pipeOption{
|
option: pipeOption{
|
||||||
limit: -1,
|
limit: -1,
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,3 +25,17 @@ func (r *Reader) ReadMultiBufferTimeout(d time.Duration) (buf.MultiBuffer, error
|
||||||
func (r *Reader) Interrupt() {
|
func (r *Reader) Interrupt() {
|
||||||
r.pipe.Interrupt()
|
r.pipe.Interrupt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReturnAnError makes ReadMultiBuffer return an error, only once.
|
||||||
|
func (r *Reader) ReturnAnError(err error) {
|
||||||
|
r.pipe.errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recover catches an error set by ReturnAnError, if exists.
|
||||||
|
func (r *Reader) Recover() (err error) {
|
||||||
|
select {
|
||||||
|
case err = <-r.pipe.errChan:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue