mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-09-24 10:00:28 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1165fe8fcf | ||
|
|
52a412d9e2 | ||
|
|
18a1b5042a | ||
|
|
c26d2eda24 | ||
|
|
a1bf968be9 | ||
|
|
c037ccd98d |
@@ -67,9 +67,7 @@ jobs:
|
||||
check-latest: true
|
||||
cache: false
|
||||
- name: Check Format
|
||||
run: |
|
||||
go install -v mvdan.cc/gofumpt@latest
|
||||
go run ./infra/vformat/main.go -mode check -pwd ./
|
||||
run: go run ./infra/vformat/main.go -mode check -pwd ./
|
||||
|
||||
test:
|
||||
needs: check-assets
|
||||
|
||||
@@ -10,12 +10,12 @@ import (
|
||||
|
||||
// [,)
|
||||
func RandBetween(from int64, to int64) int64 {
|
||||
if from == to {
|
||||
return from
|
||||
}
|
||||
if from > to {
|
||||
from, to = to, from
|
||||
}
|
||||
if d := to - from; d == 0 || d == 1 {
|
||||
return from
|
||||
}
|
||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
|
||||
return from + bigInt.Int64()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/xtls/xray-core/common/ctx"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
)
|
||||
|
||||
//go:linkname IndependentCancelCtx context.newCancelCtx
|
||||
@@ -20,14 +19,13 @@ const (
|
||||
isReverseMuxKey ctx.SessionKey = 4 // is reverse mux
|
||||
sockoptSessionKey ctx.SessionKey = 5 // used by dokodemo to only receive sockopt.Mark
|
||||
trackedConnectionErrorKey ctx.SessionKey = 6 // used by observer to get outbound error
|
||||
dispatcherKey ctx.SessionKey = 7 // used by ss2022 inbounds to get dispatcher
|
||||
timeoutOnlyKey ctx.SessionKey = 8 // mux context's child contexts to only cancel when its own traffic times out
|
||||
allowedNetworkKey ctx.SessionKey = 9 // muxcool server control incoming request tcp/udp
|
||||
fullHandlerKey ctx.SessionKey = 10 // outbound gets full handler
|
||||
mitmAlpn11Key ctx.SessionKey = 11 // used by TLS dialer
|
||||
mitmServerNameKey ctx.SessionKey = 12 // used by TLS dialer
|
||||
timeoutOnlyKey ctx.SessionKey = 7 // mux context's child contexts to only cancel when its own traffic times out
|
||||
allowedNetworkKey ctx.SessionKey = 8 // muxcool server control incoming request tcp/udp
|
||||
fullHandlerKey ctx.SessionKey = 9 // outbound gets full handler
|
||||
mitmAlpn11Key ctx.SessionKey = 10 // used by TLS dialer
|
||||
mitmServerNameKey ctx.SessionKey = 11 // used by TLS dialer
|
||||
|
||||
streamSettingsKey ctx.SessionKey = 13
|
||||
streamSettingsKey ctx.SessionKey = 12
|
||||
)
|
||||
|
||||
func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context {
|
||||
@@ -129,17 +127,6 @@ func TrackedConnectionError(ctx context.Context, tracker TrackedRequestErrorFeed
|
||||
return context.WithValue(ctx, trackedConnectionErrorKey, tracker)
|
||||
}
|
||||
|
||||
func ContextWithDispatcher(ctx context.Context, dispatcher routing.Dispatcher) context.Context {
|
||||
return context.WithValue(ctx, dispatcherKey, dispatcher)
|
||||
}
|
||||
|
||||
func DispatcherFromContext(ctx context.Context) routing.Dispatcher {
|
||||
if dispatcher, ok := ctx.Value(dispatcherKey).(routing.Dispatcher); ok {
|
||||
return dispatcher
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ContextWithTimeoutOnly(ctx context.Context, only bool) context.Context {
|
||||
return context.WithValue(ctx, timeoutOnlyKey, only)
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package singbridge
|
||||
|
||||
import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
)
|
||||
|
||||
func ToNetwork(network string) net.Network {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
return net.Network_TCP
|
||||
case N.NetworkUDP:
|
||||
return net.Network_UDP
|
||||
default:
|
||||
return net.Network_Unknown
|
||||
}
|
||||
}
|
||||
|
||||
func ToDestination(socksaddr M.Socksaddr, network net.Network) (net.Destination, error) {
|
||||
// IsFqdn() implicitly checks if the domain name is valid
|
||||
if socksaddr.IsFqdn() {
|
||||
return net.Destination{
|
||||
Network: network,
|
||||
Address: net.DomainAddress(socksaddr.Fqdn),
|
||||
Port: net.Port(socksaddr.Port),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsIP() implicitly checks if the IP address is valid
|
||||
if socksaddr.IsIP() {
|
||||
return net.Destination{
|
||||
Network: network,
|
||||
Address: net.IPAddress(socksaddr.Addr.AsSlice()),
|
||||
Port: net.Port(socksaddr.Port),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return net.Destination{}, errors.New("invalid socks address: ", socksaddr)
|
||||
}
|
||||
|
||||
func ToSocksaddr(destination net.Destination) M.Socksaddr {
|
||||
var addr M.Socksaddr
|
||||
switch destination.Address.Family() {
|
||||
case net.AddressFamilyDomain:
|
||||
addr.Fqdn = destination.Address.Domain()
|
||||
default:
|
||||
addr.Addr = M.AddrFromIP(destination.Address.IP())
|
||||
}
|
||||
addr.Port = uint16(destination.Port)
|
||||
return addr
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package singbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/proxy"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/pipe"
|
||||
)
|
||||
|
||||
var _ N.Dialer = (*XrayDialer)(nil)
|
||||
|
||||
type XrayDialer struct {
|
||||
internet.Dialer
|
||||
}
|
||||
|
||||
func NewDialer(dialer internet.Dialer) *XrayDialer {
|
||||
return &XrayDialer{dialer}
|
||||
}
|
||||
|
||||
func (d *XrayDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
dest, err := ToDestination(destination, ToNetwork(network))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.Dialer.Dial(ctx, dest)
|
||||
}
|
||||
|
||||
func (d *XrayDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
type XrayOutboundDialer struct {
|
||||
outbound proxy.Outbound
|
||||
dialer internet.Dialer
|
||||
}
|
||||
|
||||
func NewOutboundDialer(outbound proxy.Outbound, dialer internet.Dialer) *XrayOutboundDialer {
|
||||
return &XrayOutboundDialer{outbound, dialer}
|
||||
}
|
||||
|
||||
func (d *XrayOutboundDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
dest, err := ToDestination(destination, ToNetwork(network))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
if len(outbounds) == 0 {
|
||||
outbounds = []*session.Outbound{{}}
|
||||
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||
}
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
ob.Target = dest
|
||||
|
||||
opts := []pipe.Option{pipe.WithSizeLimit(64 * 1024)}
|
||||
uplinkReader, uplinkWriter := pipe.New(opts...)
|
||||
downlinkReader, downlinkWriter := pipe.New(opts...)
|
||||
conn := cnc.NewConnection(cnc.ConnectionInputMulti(downlinkWriter), cnc.ConnectionOutputMulti(uplinkReader))
|
||||
go d.outbound.Process(ctx, &transport.Link{Reader: downlinkReader, Writer: uplinkWriter}, d.dialer)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (d *XrayOutboundDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package singbridge
|
||||
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
func ReturnError(err error) error {
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package singbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
)
|
||||
|
||||
var (
|
||||
_ N.TCPConnectionHandler = (*Dispatcher)(nil)
|
||||
_ N.UDPConnectionHandler = (*Dispatcher)(nil)
|
||||
)
|
||||
|
||||
type Dispatcher struct {
|
||||
upstream routing.Dispatcher
|
||||
newErrorFunc func(values ...any) *errors.Error
|
||||
}
|
||||
|
||||
func NewDispatcher(dispatcher routing.Dispatcher, newErrorFunc func(values ...any) *errors.Error) *Dispatcher {
|
||||
return &Dispatcher{
|
||||
upstream: dispatcher,
|
||||
newErrorFunc: newErrorFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dispatcher) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
dest, err := ToDestination(metadata.Destination, net.Network_TCP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
xConn := NewConn(conn)
|
||||
return d.upstream.DispatchLink(ctx, dest, &transport.Link{
|
||||
Reader: xConn,
|
||||
Writer: xConn,
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Dispatcher) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||
dest, err := ToDestination(metadata.Destination, net.Network_UDP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.upstream.DispatchLink(ctx, dest, &transport.Link{
|
||||
Reader: buf.NewPacketReader(conn.(io.Reader)),
|
||||
Writer: buf.NewWriter(conn.(io.Writer)),
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Dispatcher) NewError(ctx context.Context, err error) {
|
||||
errors.LogInfo(ctx, err.Error())
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package singbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
var _ logger.ContextLogger = (*XrayLogger)(nil)
|
||||
|
||||
type XrayLogger struct {
|
||||
newError func(values ...any) *errors.Error
|
||||
}
|
||||
|
||||
func NewLogger(newErrorFunc func(values ...any) *errors.Error) *XrayLogger {
|
||||
return &XrayLogger{
|
||||
newErrorFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *XrayLogger) Trace(args ...any) {
|
||||
}
|
||||
|
||||
func (l *XrayLogger) Debug(args ...any) {
|
||||
errors.LogDebug(context.Background(), args...)
|
||||
}
|
||||
|
||||
func (l *XrayLogger) Info(args ...any) {
|
||||
errors.LogInfo(context.Background(), args...)
|
||||
}
|
||||
|
||||
func (l *XrayLogger) Warn(args ...any) {
|
||||
errors.LogWarning(context.Background(), args...)
|
||||
}
|
||||
|
||||
func (l *XrayLogger) Error(args ...any) {
|
||||
errors.LogError(context.Background(), args...)
|
||||
}
|
||||
|
||||
func (l *XrayLogger) Fatal(args ...any) {
|
||||
}
|
||||
|
||||
func (l *XrayLogger) Panic(args ...any) {
|
||||
}
|
||||
|
||||
func (l *XrayLogger) TraceContext(ctx context.Context, args ...any) {
|
||||
}
|
||||
|
||||
func (l *XrayLogger) DebugContext(ctx context.Context, args ...any) {
|
||||
errors.LogDebug(ctx, args...)
|
||||
}
|
||||
|
||||
func (l *XrayLogger) InfoContext(ctx context.Context, args ...any) {
|
||||
errors.LogInfo(ctx, args...)
|
||||
}
|
||||
|
||||
func (l *XrayLogger) WarnContext(ctx context.Context, args ...any) {
|
||||
errors.LogWarning(ctx, args...)
|
||||
}
|
||||
|
||||
func (l *XrayLogger) ErrorContext(ctx context.Context, args ...any) {
|
||||
errors.LogError(ctx, args...)
|
||||
}
|
||||
|
||||
func (l *XrayLogger) FatalContext(ctx context.Context, args ...any) {
|
||||
}
|
||||
|
||||
func (l *XrayLogger) PanicContext(ctx context.Context, args ...any) {
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package singbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
B "github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
)
|
||||
|
||||
func CopyPacketConn(ctx context.Context, inboundConn net.Conn, link *transport.Link, destination net.Destination, serverConn net.PacketConn) error {
|
||||
cancel := func() {
|
||||
common.Interrupt(link.Reader)
|
||||
common.Interrupt(serverConn)
|
||||
}
|
||||
conn := &PacketConnWrapper{
|
||||
Reader: link.Reader,
|
||||
Writer: link.Writer,
|
||||
Dest: destination,
|
||||
Conn: inboundConn,
|
||||
T: signal.CancelAfterInactivity(ctx, cancel, 300*time.Second),
|
||||
}
|
||||
return ReturnError(bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(serverConn)))
|
||||
}
|
||||
|
||||
type PacketConnWrapper struct {
|
||||
buf.Reader
|
||||
buf.Writer
|
||||
net.Conn
|
||||
Dest net.Destination
|
||||
cached buf.MultiBuffer
|
||||
|
||||
// A simple patch to avoid goroutine leak since sing infra cannot awake read block by write err
|
||||
T *signal.ActivityTimer
|
||||
}
|
||||
|
||||
func (w *PacketConnWrapper) ReadPacket(buffer *B.Buffer) (addr M.Socksaddr, err error) {
|
||||
w.T.Update()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// uplinkonly
|
||||
w.T.SetTimeout(2 * time.Second)
|
||||
}
|
||||
}()
|
||||
if w.cached != nil {
|
||||
mb, bb := buf.SplitFirst(w.cached)
|
||||
if bb == nil {
|
||||
w.cached = nil
|
||||
} else {
|
||||
buffer.Write(bb.Bytes())
|
||||
w.cached = mb
|
||||
var destination net.Destination
|
||||
if bb.UDP != nil {
|
||||
destination = *bb.UDP
|
||||
} else {
|
||||
destination = w.Dest
|
||||
}
|
||||
bb.Release()
|
||||
return ToSocksaddr(destination), nil
|
||||
}
|
||||
}
|
||||
mb, err := w.ReadMultiBuffer()
|
||||
nb, bb := buf.SplitFirst(mb)
|
||||
if bb == nil {
|
||||
return M.Socksaddr{}, nil
|
||||
} else {
|
||||
buffer.Write(bb.Bytes())
|
||||
w.cached = nb
|
||||
var destination net.Destination
|
||||
if bb.UDP != nil {
|
||||
destination = *bb.UDP
|
||||
} else {
|
||||
destination = w.Dest
|
||||
}
|
||||
bb.Release()
|
||||
return ToSocksaddr(destination), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *PacketConnWrapper) WritePacket(buffer *B.Buffer, destination M.Socksaddr) (err error) {
|
||||
w.T.Update()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// downlinkonly
|
||||
w.T.SetTimeout(5 * time.Second)
|
||||
}
|
||||
}()
|
||||
endpoint, err := ToDestination(destination, net.Network_UDP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vBuf := buf.New()
|
||||
vBuf.Write(buffer.Bytes())
|
||||
vBuf.UDP = &endpoint
|
||||
return w.WriteMultiBuffer(buf.MultiBuffer{vBuf})
|
||||
}
|
||||
|
||||
func (w *PacketConnWrapper) Close() error {
|
||||
buf.ReleaseMulti(w.cached)
|
||||
return nil
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package singbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
)
|
||||
|
||||
func CopyConn(ctx context.Context, inboundConn net.Conn, link *transport.Link, serverConn net.Conn) error {
|
||||
conn := &PipeConnWrapper{
|
||||
W: link.Writer,
|
||||
Conn: inboundConn,
|
||||
}
|
||||
if ir, ok := link.Reader.(io.Reader); ok {
|
||||
conn.R = ir
|
||||
} else {
|
||||
conn.R = &buf.BufferedReader{Reader: link.Reader}
|
||||
}
|
||||
cancel := func() {
|
||||
common.Interrupt(link.Reader)
|
||||
common.Interrupt(serverConn)
|
||||
}
|
||||
conn.T = signal.CancelAfterInactivity(ctx, cancel, 300*time.Second)
|
||||
return ReturnError(bufio.CopyConn(ctx, conn, serverConn))
|
||||
}
|
||||
|
||||
type PipeConnWrapper struct {
|
||||
R io.Reader
|
||||
W buf.Writer
|
||||
net.Conn
|
||||
|
||||
// A simple patch to avoid goroutine leak since sing infra cannot awake read block by write err
|
||||
T *signal.ActivityTimer
|
||||
}
|
||||
|
||||
func (w *PipeConnWrapper) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *PipeConnWrapper) Read(b []byte) (n int, err error) {
|
||||
w.T.Update()
|
||||
n, err = w.R.Read(b)
|
||||
if err != nil {
|
||||
// uplinkonly
|
||||
w.T.SetTimeout(2 * time.Second)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *PipeConnWrapper) Write(p []byte) (n int, err error) {
|
||||
w.T.Update()
|
||||
n = len(p)
|
||||
var mb buf.MultiBuffer
|
||||
pLen := len(p)
|
||||
for pLen > 0 {
|
||||
buffer := buf.New()
|
||||
if pLen > buf.Size {
|
||||
_, err = buffer.Write(p[:buf.Size])
|
||||
p = p[buf.Size:]
|
||||
} else {
|
||||
buffer.Write(p)
|
||||
}
|
||||
pLen -= int(buffer.Len())
|
||||
mb = append(mb, buffer)
|
||||
}
|
||||
err = w.W.WriteMultiBuffer(mb)
|
||||
if err != nil {
|
||||
n = 0
|
||||
buf.ReleaseMulti(mb)
|
||||
// downlinkonly
|
||||
w.T.SetTimeout(5 * time.Second)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package singbridge
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
)
|
||||
|
||||
var (
|
||||
_ buf.Reader = (*Conn)(nil)
|
||||
_ buf.TimeoutReader = (*Conn)(nil)
|
||||
_ buf.Writer = (*Conn)(nil)
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
writer N.VectorisedWriter
|
||||
}
|
||||
|
||||
func NewConn(conn net.Conn) *Conn {
|
||||
writer, _ := bufio.CreateVectorisedWriter(conn)
|
||||
return &Conn{
|
||||
Conn: conn,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
buffer, err := buf.ReadBuffer(c.Conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.MultiBuffer{buffer}, nil
|
||||
}
|
||||
|
||||
func (c *Conn) ReadMultiBufferTimeout(duration time.Duration) (buf.MultiBuffer, error) {
|
||||
err := c.SetReadDeadline(time.Now().Add(duration))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.SetReadDeadline(time.Time{})
|
||||
return c.ReadMultiBuffer()
|
||||
}
|
||||
|
||||
func (c *Conn) WriteMultiBuffer(bufferList buf.MultiBuffer) error {
|
||||
defer buf.ReleaseMulti(bufferList)
|
||||
if c.writer != nil {
|
||||
bytesList := make([][]byte, len(bufferList))
|
||||
for i, buffer := range bufferList {
|
||||
bytesList[i] = buffer.Bytes()
|
||||
}
|
||||
return common.Error(bufio.WriteVectorised(c.writer, bytesList))
|
||||
}
|
||||
// Since this conn is only used by tun, we don't force buffer writes to merge.
|
||||
for _, buffer := range bufferList {
|
||||
_, err := c.Conn.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@ import (
|
||||
var (
|
||||
Version_x byte = 26
|
||||
Version_y byte = 9
|
||||
Version_z byte = 8
|
||||
Version_z byte = 9
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -18,8 +18,6 @@ require (
|
||||
github.com/pires/go-proxyproto v0.15.0
|
||||
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/sagernet/sing v0.5.1
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
github.com/xtls/reality v0.0.0-20260908062103-8cdf7bf9c7f0
|
||||
@@ -37,6 +35,7 @@ require (
|
||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
||||
h12.io/socks v1.0.3
|
||||
lukechampine.com/blake3 v1.4.1
|
||||
mvdan.cc/gofumpt v0.12.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -48,7 +47,6 @@ require (
|
||||
github.com/juju/ratelimit v1.0.2 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/koron/go-ssdp v0.0.4 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/libp2p/go-netroute v0.2.1 // indirect
|
||||
github.com/pion/dtls/v3 v3.1.5 // indirect
|
||||
github.com/pion/logging v0.2.4 // indirect
|
||||
@@ -59,6 +57,7 @@ require (
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.49.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
@@ -6,13 +6,14 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/circl v1.6.5 h1:O64F26HEqNhznd/hrC5KZXVKYuKM2rx4deZDTc4ihQA=
|
||||
github.com/cloudflare/circl v1.6.5/go.mod h1:h5LNyxAc5nTue9DS5jT+48en2PSDYt3zdGnz5OstK6c=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-quicktest/qt v1.102.0 h1:HSQxCeh5YZH3EL3W39ixjtyaEhcWSXQHtHnMBzSs474=
|
||||
github.com/go-quicktest/qt v1.102.0/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI=
|
||||
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
@@ -73,12 +74,8 @@ github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af h1:er
|
||||
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
|
||||
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||
github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g=
|
||||
github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
|
||||
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
|
||||
@@ -147,6 +144,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
|
||||
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -176,3 +175,5 @@ h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo=
|
||||
h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
||||
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
|
||||
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
|
||||
mvdan.cc/gofumpt v0.12.0 h1:1Lbudkz2kpM9Cjz2pL4M19u7q+GaEhCTNf7N9mfpcho=
|
||||
mvdan.cc/gofumpt v0.12.0/go.mod h1:SmBHHrljiZu/uoypeKup3rFzP6eoC9UwCp2iH5E3jZA=
|
||||
|
||||
+4
-104
@@ -3,14 +3,11 @@ package conf
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
C "github.com/sagernet/sing/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/proxy/shadowsocks"
|
||||
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -30,12 +27,10 @@ func cipherFromString(c string) shadowsocks.CipherType {
|
||||
}
|
||||
|
||||
type ShadowsocksUserConfig struct {
|
||||
Cipher string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
Level byte `json:"level"`
|
||||
Email string `json:"email"`
|
||||
Address *Address `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
Cipher string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
Level byte `json:"level"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type ShadowsocksServerConfig struct {
|
||||
@@ -55,10 +50,6 @@ func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
|
||||
v.Users = v.Clients
|
||||
}
|
||||
|
||||
if C.Contains(shadowaead_2022.List, v.Cipher) {
|
||||
return buildShadowsocks2022(v)
|
||||
}
|
||||
|
||||
config := new(shadowsocks.ServerConfig)
|
||||
config.Network = v.NetworkList.Build()
|
||||
|
||||
@@ -110,72 +101,6 @@ func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func buildShadowsocks2022(v *ShadowsocksServerConfig) (proto.Message, error) {
|
||||
if len(v.Users) == 0 {
|
||||
config := new(shadowsocks_2022.ServerConfig)
|
||||
config.Method = v.Cipher
|
||||
config.Key = v.Password
|
||||
config.Network = v.NetworkList.Build()
|
||||
config.Email = v.Email
|
||||
return config, nil
|
||||
}
|
||||
|
||||
if v.Cipher == "" {
|
||||
return nil, errors.New("shadowsocks 2022 (multi-user): missing server method")
|
||||
}
|
||||
if !strings.Contains(v.Cipher, "aes") {
|
||||
return nil, errors.New("shadowsocks 2022 (multi-user): only blake3-aes-*-gcm methods are supported")
|
||||
}
|
||||
|
||||
if v.Users[0].Address == nil {
|
||||
config := new(shadowsocks_2022.MultiUserServerConfig)
|
||||
config.Method = v.Cipher
|
||||
config.Key = v.Password
|
||||
config.Network = v.NetworkList.Build()
|
||||
|
||||
config.Users = make([]*protocol.User, len(v.Users))
|
||||
processUser := func(idx int) error {
|
||||
user := v.Users[idx]
|
||||
if user.Cipher != "" {
|
||||
return errors.New("shadowsocks 2022 (multi-user): users must have empty method")
|
||||
}
|
||||
account := &shadowsocks_2022.Account{
|
||||
Key: user.Password,
|
||||
}
|
||||
config.Users[idx] = &protocol.User{
|
||||
Email: user.Email,
|
||||
Level: uint32(user.Level),
|
||||
Account: serial.ToTypedMessage(account),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := task.ParallelForN(len(v.Users), processUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
config := new(shadowsocks_2022.RelayServerConfig)
|
||||
config.Method = v.Cipher
|
||||
config.Key = v.Password
|
||||
config.Network = v.NetworkList.Build()
|
||||
for _, user := range v.Users {
|
||||
if user.Cipher != "" {
|
||||
return nil, errors.New("shadowsocks 2022 (relay): users must have empty method")
|
||||
}
|
||||
if user.Address == nil {
|
||||
return nil, errors.New("shadowsocks 2022 (relay): all users must have relay address")
|
||||
}
|
||||
config.Destinations = append(config.Destinations, &shadowsocks_2022.RelayDestination{
|
||||
Key: user.Password,
|
||||
Email: user.Email,
|
||||
Address: user.Address.Build(),
|
||||
Port: uint32(user.Port),
|
||||
})
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
type ShadowsocksServerTarget struct {
|
||||
Address *Address `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
@@ -214,33 +139,8 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New(`Shadowsocks settings: "servers" should have one and only one member. Multiple endpoints in "servers" should use multiple Shadowsocks outbounds and routing balancer instead`)
|
||||
}
|
||||
|
||||
if len(v.Servers) == 1 {
|
||||
server := v.Servers[0]
|
||||
if C.Contains(shadowaead_2022.List, server.Cipher) {
|
||||
if server.Address == nil {
|
||||
return nil, errors.New("Shadowsocks server address is not set.")
|
||||
}
|
||||
if server.Port == 0 {
|
||||
return nil, errors.New("Invalid Shadowsocks port.")
|
||||
}
|
||||
if server.Password == "" {
|
||||
return nil, errors.New("Shadowsocks password is not specified.")
|
||||
}
|
||||
|
||||
config := new(shadowsocks_2022.ClientConfig)
|
||||
config.Address = server.Address.Build()
|
||||
config.Port = uint32(server.Port)
|
||||
config.Method = server.Cipher
|
||||
config.Key = server.Password
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
|
||||
config := new(shadowsocks.ClientConfig)
|
||||
for _, server := range v.Servers {
|
||||
if C.Contains(shadowaead_2022.List, server.Cipher) {
|
||||
return nil, errors.New("Shadowsocks 2022 accept no multi servers")
|
||||
}
|
||||
if server.Address == nil {
|
||||
return nil, errors.New("Shadowsocks server address is not set.")
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
googleuuid "github.com/google/uuid"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/fragment"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/header/custom"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/realm"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/sudoku"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/udphop"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/xdns"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/xicmp"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask/xmc"
|
||||
@@ -83,6 +85,7 @@ var (
|
||||
"xdns": func() interface{} { return new(Xdns) },
|
||||
"xicmp": func() interface{} { return new(Xicmp) },
|
||||
"realm": func() interface{} { return new(Realm) },
|
||||
"udphop": func() interface{} { return new(UDPHop) },
|
||||
}, "type", "settings")
|
||||
)
|
||||
|
||||
@@ -905,6 +908,62 @@ func (c *Realm) Build() (proto.Message, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
type UDPHop struct {
|
||||
Sockopt *SocketConfig `json:"sockopt"`
|
||||
Mode string `json:"mode"`
|
||||
Interval Int32Range `json:"interval"`
|
||||
RemotePorts PortList `json:"remotePorts"`
|
||||
RemoteIPs []string `json:"remoteIPs"`
|
||||
}
|
||||
|
||||
func (c *UDPHop) Build() (proto.Message, error) {
|
||||
var sockopt *internet.SocketConfig
|
||||
if c.Sockopt != nil {
|
||||
var err error
|
||||
sockopt, err = c.Sockopt.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var local, remote, remoteOnce bool
|
||||
for _, mode := range strings.Split(c.Mode, ",") {
|
||||
switch strings.ToLower(mode) {
|
||||
case "intervallocal":
|
||||
local = true
|
||||
case "intervalremote":
|
||||
remote = true
|
||||
case "perconnremote":
|
||||
remoteOnce = true
|
||||
default:
|
||||
return nil, errors.New("invalid mode ", mode)
|
||||
}
|
||||
}
|
||||
var remoteIPs []string
|
||||
for _, ip := range c.RemoteIPs {
|
||||
prefix, err := netip.ParsePrefix(ip)
|
||||
if err == nil {
|
||||
remoteIPs = append(remoteIPs, prefix.String())
|
||||
continue
|
||||
}
|
||||
addr, err := netip.ParseAddr(ip)
|
||||
if err == nil {
|
||||
remoteIPs = append(remoteIPs, netip.PrefixFrom(addr, addr.BitLen()).String())
|
||||
continue
|
||||
}
|
||||
return nil, errors.New("invalid ip ", ip)
|
||||
}
|
||||
return &udphop.Config{
|
||||
Sockopt: sockopt,
|
||||
Local: local,
|
||||
Remote: remote,
|
||||
RemoteOnce: remoteOnce,
|
||||
IntervalMin: int64(c.Interval.From),
|
||||
IntervalMax: int64(c.Interval.To),
|
||||
RemotePorts: c.RemotePorts.Build().Ports(),
|
||||
RemoteIPs: remoteIPs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Mask struct {
|
||||
Type string `json:"type"`
|
||||
Settings *json.RawMessage `json:"settings"`
|
||||
@@ -938,7 +997,6 @@ type QuicParamsConfig struct {
|
||||
BrutalUp Bandwidth `json:"brutalUp"`
|
||||
BrutalDown Bandwidth `json:"brutalDown"`
|
||||
BrutalDisableLossCompensation bool `json:"brutalDisableLossCompensation"`
|
||||
UdpHop UdpHop `json:"udpHop"`
|
||||
InitStreamReceiveWindow uint64 `json:"initStreamReceiveWindow"`
|
||||
MaxStreamReceiveWindow uint64 `json:"maxStreamReceiveWindow"`
|
||||
InitConnectionReceiveWindow uint64 `json:"initConnectionReceiveWindow"`
|
||||
|
||||
@@ -253,10 +253,6 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
||||
return nil, errors.New("unknown congestion control: ", c.FinalMask.QuicParams.Congestion, ", valid values: reno, bbr, brutal, force-brutal")
|
||||
}
|
||||
|
||||
if (c.FinalMask.QuicParams.UdpHop.Interval.From != 0 && c.FinalMask.QuicParams.UdpHop.Interval.From < 5) || (c.FinalMask.QuicParams.UdpHop.Interval.To != 0 && c.FinalMask.QuicParams.UdpHop.Interval.To < 5) {
|
||||
return nil, errors.New("Interval must be at least 5")
|
||||
}
|
||||
|
||||
if c.FinalMask.QuicParams.InitStreamReceiveWindow > 0 && c.FinalMask.QuicParams.InitStreamReceiveWindow < 16384 {
|
||||
return nil, errors.New("InitStreamReceiveWindow must be at least 16384")
|
||||
}
|
||||
@@ -290,22 +286,17 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
||||
BrutalUp: up,
|
||||
BrutalDown: down,
|
||||
BrutalDisableLossCompensation: c.FinalMask.QuicParams.BrutalDisableLossCompensation,
|
||||
UdpHop: &internet.UdpHop{
|
||||
Ports: c.FinalMask.QuicParams.UdpHop.PortList.Build().Ports(),
|
||||
IntervalMin: int64(c.FinalMask.QuicParams.UdpHop.Interval.From),
|
||||
IntervalMax: int64(c.FinalMask.QuicParams.UdpHop.Interval.To),
|
||||
},
|
||||
InitStreamReceiveWindow: c.FinalMask.QuicParams.InitStreamReceiveWindow,
|
||||
MaxStreamReceiveWindow: c.FinalMask.QuicParams.MaxStreamReceiveWindow,
|
||||
InitConnReceiveWindow: c.FinalMask.QuicParams.InitConnectionReceiveWindow,
|
||||
MaxConnReceiveWindow: c.FinalMask.QuicParams.MaxConnectionReceiveWindow,
|
||||
MaxIdleTimeout: c.FinalMask.QuicParams.MaxIdleTimeout,
|
||||
KeepAlivePeriod: c.FinalMask.QuicParams.KeepAlivePeriod,
|
||||
DisablePathMtuDiscovery: c.FinalMask.QuicParams.DisablePathMTUDiscovery,
|
||||
DisableChromeParrot: c.FinalMask.QuicParams.DisableChromeParrot,
|
||||
DisableGSO: c.FinalMask.QuicParams.DisableGSO,
|
||||
MaxIncomingStreams: c.FinalMask.QuicParams.MaxIncomingStreams,
|
||||
DisableStatelessReset: c.FinalMask.QuicParams.DisableStatelessReset,
|
||||
InitStreamReceiveWindow: c.FinalMask.QuicParams.InitStreamReceiveWindow,
|
||||
MaxStreamReceiveWindow: c.FinalMask.QuicParams.MaxStreamReceiveWindow,
|
||||
InitConnReceiveWindow: c.FinalMask.QuicParams.InitConnectionReceiveWindow,
|
||||
MaxConnReceiveWindow: c.FinalMask.QuicParams.MaxConnectionReceiveWindow,
|
||||
MaxIdleTimeout: c.FinalMask.QuicParams.MaxIdleTimeout,
|
||||
KeepAlivePeriod: c.FinalMask.QuicParams.KeepAlivePeriod,
|
||||
DisablePathMtuDiscovery: c.FinalMask.QuicParams.DisablePathMTUDiscovery,
|
||||
DisableChromeParrot: c.FinalMask.QuicParams.DisableChromeParrot,
|
||||
DisableGSO: c.FinalMask.QuicParams.DisableGSO,
|
||||
MaxIncomingStreams: c.FinalMask.QuicParams.MaxIncomingStreams,
|
||||
DisableStatelessReset: c.FinalMask.QuicParams.DisableStatelessReset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"net/url"
|
||||
@@ -534,10 +533,6 @@ type KCPConfig struct {
|
||||
|
||||
// Build implements Buildable.
|
||||
func (c *KCPConfig) Build() (proto.Message, error) {
|
||||
if c.HeaderConfig != nil || c.Seed != nil {
|
||||
return nil, errors.PrintRemovedFeatureError("mkcp header & seed", "finalmask/udp header-* & mkcp-original & mkcp-aes128gcm")
|
||||
}
|
||||
|
||||
config := common.Must2(internet.CreateTransportConfig(kcp.ProtocolName)).(*kcp.Config)
|
||||
|
||||
if c.Mtu != nil {
|
||||
@@ -560,16 +555,16 @@ func (c *KCPConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
|
||||
if config.Mtu < 21 {
|
||||
return nil, errors.New("Mtu must be at least 21").AtError()
|
||||
return nil, errors.New("MTU must be at least 21")
|
||||
}
|
||||
if config.Tti < 10 || config.Tti > 1000 {
|
||||
return nil, errors.New("invalid mKCP TTI: ", c.Tti).AtError()
|
||||
return nil, errors.New("TTI must be between 10 and 1000")
|
||||
}
|
||||
if config.CwndMultiplier < 1 {
|
||||
return nil, errors.New("CwndMultiplier must be at least 1").AtError()
|
||||
return nil, errors.New("CwndMultiplier must be at least 1")
|
||||
}
|
||||
if config.GetSendingBufferSize() == 0 {
|
||||
return nil, errors.New("MaxSendingWindow must be >= Mtu").AtError()
|
||||
return nil, errors.New("MaxSendingWindow must be at least ", config.Mtu)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
@@ -739,11 +734,6 @@ func (b Bandwidth) Bps() (uint64, error) {
|
||||
return uint64(val*float64(mul)) / 8, nil
|
||||
}
|
||||
|
||||
type UdpHop struct {
|
||||
PortList PortList `json:"ports"`
|
||||
Interval Int32Range `json:"interval"`
|
||||
}
|
||||
|
||||
type Masquerade struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
@@ -760,14 +750,8 @@ type Masquerade struct {
|
||||
}
|
||||
|
||||
type HysteriaConfig struct {
|
||||
Version int32 `json:"version"`
|
||||
Auth string `json:"auth"`
|
||||
|
||||
Congestion *string `json:"congestion"`
|
||||
Up *Bandwidth `json:"up"`
|
||||
Down *Bandwidth `json:"down"`
|
||||
UdpHop *UdpHop `json:"udphop"`
|
||||
|
||||
Version int32 `json:"version"`
|
||||
Auth string `json:"auth"`
|
||||
UdpIdleTimeout int64 `json:"udpIdleTimeout"`
|
||||
Masquerade Masquerade `json:"masquerade"`
|
||||
}
|
||||
@@ -777,10 +761,6 @@ func (c *HysteriaConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New("version != 2")
|
||||
}
|
||||
|
||||
if c.Congestion != nil || c.Up != nil || c.Down != nil || c.UdpHop != nil {
|
||||
errors.LogWarning(context.Background(), "congestion & up & down & udphop move to finalmask/quicParams")
|
||||
}
|
||||
|
||||
if c.UdpIdleTimeout != 0 && (c.UdpIdleTimeout < 2 || c.UdpIdleTimeout > 600) {
|
||||
return nil, errors.New("UdpIdleTimeout must be between 2 and 600")
|
||||
}
|
||||
|
||||
@@ -312,6 +312,9 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||
return nil, errors.New(`VLESS users: invalid user`).Base(err)
|
||||
}
|
||||
// validateOutboundTransportSecurity needs to see these
|
||||
c.Encryption = account.Encryption
|
||||
c.Address = rec.Address
|
||||
if account.Reverse != nil { // may not be reached: error json unmarshal
|
||||
return nil, errors.New(`VLESS users: please use simplified outbound's config style to use "reverse"`)
|
||||
}
|
||||
|
||||
+1
-1
@@ -242,7 +242,7 @@ func validateOutboundTransportSecurity(rawConfig interface{}, senderSettings *pr
|
||||
if vlessCfg.Encryption != "" && vlessCfg.Encryption != "none" {
|
||||
return nil
|
||||
}
|
||||
if requiresTransportSecurity(vlessCfg.Vnext[0].Address) {
|
||||
if requiresTransportSecurity(vlessCfg.Address) {
|
||||
return errors.New("vless without TLS or other encryption is prohibited unless the server address is a private IP or domain")
|
||||
}
|
||||
}
|
||||
|
||||
+273
-139
@@ -1,15 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"mvdan.cc/gofumpt/format"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -23,101 +26,27 @@ var (
|
||||
isFormat bool
|
||||
)
|
||||
|
||||
// envFile returns the name of the Go environment configuration file.
|
||||
// Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166
|
||||
func envFile() (string, error) {
|
||||
if file := os.Getenv("GOENV"); file != "" {
|
||||
if file == "off" {
|
||||
return "", errors.New("GOENV=off")
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
dir, err := os.UserConfigDir()
|
||||
func getModuleInfo(pwd string) (modPath, langVersion string, err error) {
|
||||
data, err := os.ReadFile(filepath.Join(pwd, "go.mod"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
if dir == "" {
|
||||
return "", errors.New("missing user-config dir")
|
||||
}
|
||||
return filepath.Join(dir, "go", "env"), nil
|
||||
}
|
||||
|
||||
// GetRuntimeEnv returns the value of runtime environment variable,
|
||||
// that is set by running following command: `go env -w key=value`.
|
||||
func GetRuntimeEnv(key string) (string, error) {
|
||||
file, err := envFile()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if file == "" {
|
||||
return "", errors.New("missing runtime env file")
|
||||
}
|
||||
var data []byte
|
||||
var runtimeEnv string
|
||||
data, readErr := os.ReadFile(file)
|
||||
if readErr != nil {
|
||||
return "", readErr
|
||||
}
|
||||
envStrings := strings.Split(string(data), "\n")
|
||||
for _, envItem := range envStrings {
|
||||
envItem = strings.TrimSuffix(envItem, "\r")
|
||||
envKeyValue := strings.Split(envItem, "=")
|
||||
if len(envKeyValue) == 2 && strings.TrimSpace(envKeyValue[0]) == key {
|
||||
runtimeEnv = strings.TrimSpace(envKeyValue[1])
|
||||
}
|
||||
}
|
||||
return runtimeEnv, nil
|
||||
}
|
||||
|
||||
// GetGOBIN returns GOBIN environment variable as a string. It will NOT be empty.
|
||||
func GetGOBIN() string {
|
||||
// The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command`
|
||||
GOBIN := os.Getenv("GOBIN")
|
||||
if GOBIN == "" {
|
||||
var err error
|
||||
// The one set by user by running `go env -w GOBIN=/path`
|
||||
GOBIN, err = GetRuntimeEnv("GOBIN")
|
||||
if err != nil {
|
||||
// The default one that Golang uses
|
||||
return filepath.Join(build.Default.GOPATH, "bin")
|
||||
}
|
||||
if GOBIN == "" {
|
||||
return filepath.Join(build.Default.GOPATH, "bin")
|
||||
}
|
||||
return GOBIN
|
||||
}
|
||||
return GOBIN
|
||||
}
|
||||
|
||||
func Run(binary string, args []string) ([]byte, error) {
|
||||
cmd := exec.Command(binary, args...)
|
||||
cmd.Env = append(cmd.Env, os.Environ()...)
|
||||
output, cmdErr := cmd.CombinedOutput()
|
||||
if cmdErr != nil {
|
||||
return nil, cmdErr
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func RunMany(binary string, args, files []string) bool {
|
||||
fmt.Println("Processing with", binary, args, "...")
|
||||
|
||||
formatRequired := false
|
||||
maxTasks := make(chan struct{}, runtime.NumCPU())
|
||||
for _, file := range files {
|
||||
maxTasks <- struct{}{}
|
||||
go func(file string) {
|
||||
output, err := Run(binary, append(args, file))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else if len(output) > 0 {
|
||||
fmt.Println(string(output))
|
||||
formatRequired = true
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 2 {
|
||||
switch fields[0] {
|
||||
case "module":
|
||||
modPath = fields[1]
|
||||
case "go":
|
||||
langVersion = "go" + strings.TrimPrefix(fields[1], "go")
|
||||
}
|
||||
<-maxTasks
|
||||
}(file)
|
||||
}
|
||||
}
|
||||
return formatRequired
|
||||
return modPath, langVersion, nil
|
||||
}
|
||||
|
||||
func formatGoSource(src []byte, opts format.Options) ([]byte, error) {
|
||||
return format.Source(src, opts)
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -150,26 +79,76 @@ func main() {
|
||||
}
|
||||
|
||||
pwd := *directory
|
||||
GOBIN := GetGOBIN()
|
||||
binPath := os.Getenv("PATH")
|
||||
pathSlice := []string{pwd, GOBIN, binPath}
|
||||
binPath = strings.Join(pathSlice, string(os.PathListSeparator))
|
||||
os.Setenv("PATH", binPath)
|
||||
|
||||
suffix := ""
|
||||
if runtime.GOOS == "windows" {
|
||||
suffix = ".exe"
|
||||
}
|
||||
gofmt := "gofumpt" + suffix
|
||||
|
||||
if gofmtPath, err := exec.LookPath(gofmt); err != nil {
|
||||
fmt.Println("Can not find", gofmt, "in system path or current working directory.")
|
||||
modPath, langVersion, modErr := getModuleInfo(pwd)
|
||||
if modErr != nil {
|
||||
fmt.Println("Error reading go.mod:", modErr)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
gofmt = gofmtPath
|
||||
}
|
||||
opts := format.Options{
|
||||
LangVersion: langVersion,
|
||||
ModulePath: modPath,
|
||||
}
|
||||
|
||||
if isFormat {
|
||||
fmt.Println("Formatting Go source files...")
|
||||
} else if isCheck {
|
||||
fmt.Println("Checking files thar are not properly formatted...")
|
||||
}
|
||||
|
||||
jobs := make(chan string, runtime.NumCPU())
|
||||
var wg sync.WaitGroup
|
||||
var formatRequired atomic.Bool
|
||||
var hasErrors atomic.Bool
|
||||
|
||||
for i := 0; i < runtime.NumCPU(); i++ {
|
||||
wg.Go(func() {
|
||||
for path := range jobs {
|
||||
src, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error reading %s: %v\n", path, err)
|
||||
hasErrors.Store(true)
|
||||
continue
|
||||
}
|
||||
|
||||
formatted, err := formatGoSource(src, opts)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error formatting %s: %v\n", path, err)
|
||||
hasErrors.Store(true)
|
||||
continue
|
||||
}
|
||||
|
||||
if !bytes.Equal(src, formatted) {
|
||||
var diffText []byte
|
||||
if isDryrun {
|
||||
newName := filepath.ToSlash(path)
|
||||
oldName := newName + ".orig"
|
||||
diffText = diff(oldName, src, newName, formatted)
|
||||
}
|
||||
if isFormat {
|
||||
info, statErr := os.Stat(path)
|
||||
if statErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error stating %s: %v\n", path, statErr)
|
||||
hasErrors.Store(true)
|
||||
continue
|
||||
}
|
||||
if writeErr := os.WriteFile(path, formatted, info.Mode().Perm()); writeErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", path, writeErr)
|
||||
hasErrors.Store(true)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
formatRequired.Store(true)
|
||||
if isDryrun && len(diffText) > 0 {
|
||||
fmt.Printf("%s\n%s", path, diffText)
|
||||
} else {
|
||||
fmt.Println(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
rawFilesSlice := make([]string, 0, 1000)
|
||||
walkErr := filepath.Walk(pwd, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -186,51 +165,206 @@ func main() {
|
||||
!strings.HasSuffix(filename, ".pb.go") &&
|
||||
!strings.Contains(dir, filepath.Join("testing", "mocks")) &&
|
||||
!strings.Contains(path, filepath.Join("main", "distro", "all", "all.go")) {
|
||||
rawFilesSlice = append(rawFilesSlice, path)
|
||||
jobs <- path
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
close(jobs)
|
||||
wg.Wait()
|
||||
|
||||
if walkErr != nil {
|
||||
fmt.Println(walkErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isFormat {
|
||||
gofmtArgs := []string{
|
||||
"-l", "-e", "-w",
|
||||
}
|
||||
if hasErrors.Load() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("Formatting Go source files...")
|
||||
RunMany(gofmt, gofmtArgs, rawFilesSlice)
|
||||
fmt.Println("Do NOT forget to commit file changes.")
|
||||
if isFormat {
|
||||
if formatRequired.Load() {
|
||||
fmt.Println("Do NOT forget to commit file changes.")
|
||||
}
|
||||
}
|
||||
|
||||
if isCheck {
|
||||
gofmtListArgs := []string{
|
||||
"-l", "-e",
|
||||
}
|
||||
|
||||
fmt.Println("Checking files thar are not properly formatted...")
|
||||
formatRequired := RunMany(gofmt, gofmtListArgs, rawFilesSlice)
|
||||
if formatRequired {
|
||||
if formatRequired.Load() {
|
||||
fmt.Println("Format problem(s) found.")
|
||||
}
|
||||
|
||||
if isDryrun {
|
||||
if formatRequired {
|
||||
gofmtShowArgs := []string{
|
||||
"-d", "-e",
|
||||
}
|
||||
RunMany(gofmt, gofmtShowArgs, rawFilesSlice)
|
||||
}
|
||||
}
|
||||
|
||||
if formatRequired {
|
||||
fmt.Println("Please run 'go install -v mvdan.cc/gofumpt@latest', then run 'go run ./infra/vformat/main.go' to format the Go source files.")
|
||||
fmt.Println("Please run 'go run ./infra/vformat/main.go' to format the Go source files.")
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Println("All Go source file format check has been passed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// diff algorithm copied from mvdan.cc/gofumpt/internal/govendor/diff
|
||||
type pair struct{ x, y int }
|
||||
|
||||
func diff(oldName string, old []byte, newName string, new []byte) []byte {
|
||||
if bytes.Equal(old, new) {
|
||||
return nil
|
||||
}
|
||||
x := diffLines(old)
|
||||
y := diffLines(new)
|
||||
|
||||
var out bytes.Buffer
|
||||
fmt.Fprintf(&out, "diff %s %s\n", oldName, newName)
|
||||
fmt.Fprintf(&out, "--- %s\n", oldName)
|
||||
fmt.Fprintf(&out, "+++ %s\n", newName)
|
||||
|
||||
var (
|
||||
done pair
|
||||
chunk pair
|
||||
count pair
|
||||
ctext []string
|
||||
)
|
||||
for _, m := range diffTgs(x, y) {
|
||||
if m.x < done.x {
|
||||
continue
|
||||
}
|
||||
start := m
|
||||
for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] {
|
||||
start.x--
|
||||
start.y--
|
||||
}
|
||||
end := m
|
||||
for end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] {
|
||||
end.x++
|
||||
end.y++
|
||||
}
|
||||
|
||||
for _, s := range x[done.x:start.x] {
|
||||
ctext = append(ctext, "-"+s)
|
||||
count.x++
|
||||
}
|
||||
for _, s := range y[done.y:start.y] {
|
||||
ctext = append(ctext, "+"+s)
|
||||
count.y++
|
||||
}
|
||||
|
||||
const C = 3
|
||||
if (end.x < len(x) || end.y < len(y)) &&
|
||||
(end.x-start.x < C || (len(ctext) > 0 && end.x-start.x < 2*C)) {
|
||||
for _, s := range x[start.x:end.x] {
|
||||
ctext = append(ctext, " "+s)
|
||||
count.x++
|
||||
count.y++
|
||||
}
|
||||
done = end
|
||||
continue
|
||||
}
|
||||
|
||||
if len(ctext) > 0 {
|
||||
n := end.x - start.x
|
||||
if n > C {
|
||||
n = C
|
||||
}
|
||||
for _, s := range x[start.x : start.x+n] {
|
||||
ctext = append(ctext, " "+s)
|
||||
count.x++
|
||||
count.y++
|
||||
}
|
||||
done = pair{start.x + n, start.y + n}
|
||||
|
||||
if count.x > 0 {
|
||||
chunk.x++
|
||||
}
|
||||
if count.y > 0 {
|
||||
chunk.y++
|
||||
}
|
||||
fmt.Fprintf(&out, "@@ -%d,%d +%d,%d @@\n", chunk.x, count.x, chunk.y, count.y)
|
||||
for _, s := range ctext {
|
||||
out.WriteString(s)
|
||||
}
|
||||
count.x = 0
|
||||
count.y = 0
|
||||
ctext = ctext[:0]
|
||||
}
|
||||
|
||||
if end.x >= len(x) && end.y >= len(y) {
|
||||
break
|
||||
}
|
||||
|
||||
chunk = pair{end.x - C, end.y - C}
|
||||
for _, s := range x[chunk.x:end.x] {
|
||||
ctext = append(ctext, " "+s)
|
||||
count.x++
|
||||
count.y++
|
||||
}
|
||||
done = end
|
||||
}
|
||||
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
func diffLines(x []byte) []string {
|
||||
l := strings.SplitAfter(string(x), "\n")
|
||||
if l[len(l)-1] == "" {
|
||||
l = l[:len(l)-1]
|
||||
} else {
|
||||
l[len(l)-1] += "\n\\ No newline at end of file\n"
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func diffTgs(x, y []string) []pair {
|
||||
m := make(map[string]int)
|
||||
for _, s := range x {
|
||||
if c := m[s]; c > -2 {
|
||||
m[s] = c - 1
|
||||
}
|
||||
}
|
||||
for _, s := range y {
|
||||
if c := m[s]; c > -8 {
|
||||
m[s] = c - 4
|
||||
}
|
||||
}
|
||||
|
||||
var xi, yi, inv []int
|
||||
for i, s := range y {
|
||||
if m[s] == -5 {
|
||||
m[s] = len(yi)
|
||||
yi = append(yi, i)
|
||||
}
|
||||
}
|
||||
for i, s := range x {
|
||||
if j, ok := m[s]; ok && j >= 0 {
|
||||
xi = append(xi, i)
|
||||
inv = append(inv, j)
|
||||
}
|
||||
}
|
||||
|
||||
J := inv
|
||||
n := len(xi)
|
||||
T := make([]int, n)
|
||||
L := make([]int, n)
|
||||
for i := range T {
|
||||
T[i] = n + 1
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
k := sort.Search(n, func(k int) bool {
|
||||
return T[k] >= J[i]
|
||||
})
|
||||
T[k] = J[i]
|
||||
L[i] = k + 1
|
||||
}
|
||||
k := 0
|
||||
for _, v := range L {
|
||||
if k < v {
|
||||
k = v
|
||||
}
|
||||
}
|
||||
seq := make([]pair, 2+k)
|
||||
seq[1+k] = pair{len(x), len(y)}
|
||||
lastj := n
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
if L[i] == k && J[i] < lastj {
|
||||
seq[k] = pair{xi[i], yi[J[i]]}
|
||||
k--
|
||||
}
|
||||
}
|
||||
seq[0] = pair{0, 0}
|
||||
return seq
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/infra/conf/serial"
|
||||
"github.com/xtls/xray-core/proxy/shadowsocks"
|
||||
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
|
||||
"github.com/xtls/xray-core/proxy/trojan"
|
||||
vlessin "github.com/xtls/xray-core/proxy/vless/inbound"
|
||||
vmessin "github.com/xtls/xray-core/proxy/vmess/inbound"
|
||||
@@ -86,8 +85,6 @@ func extractInboundUsers(inb *core.InboundHandlerConfig) []*protocol.User {
|
||||
return ty.Users
|
||||
case *shadowsocks.ServerConfig:
|
||||
return ty.Users
|
||||
case *shadowsocks_2022.MultiUserServerConfig:
|
||||
return ty.Users
|
||||
default:
|
||||
fmt.Println("unsupported inbound type")
|
||||
}
|
||||
|
||||
+16
-15
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -164,12 +163,9 @@ func getDefaultFinalRule(inbound *session.Inbound) *FinalRule {
|
||||
switch inbound.Name {
|
||||
case "vless-reverse":
|
||||
return defaultBlockAllRule
|
||||
case "vless", "vmess", "trojan", "hysteria", "wireguard":
|
||||
case "vless", "vmess", "trojan", "hysteria", "wireguard", "shadowsocks":
|
||||
return defaultBlockPrivateRule
|
||||
default:
|
||||
if strings.HasPrefix(inbound.Name, "shadowsocks") {
|
||||
return defaultBlockPrivateRule
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -190,6 +186,12 @@ func (h *Handler) matchFinalRule(network net.Network, address net.Address, port
|
||||
func (h *Handler) Init(config *Config, pm policy.Manager) error {
|
||||
h.config = config
|
||||
h.policyManager = pm
|
||||
if h.usesDialerProxy { // freedom is not the final outbound, final rules do not apply
|
||||
if len(config.FinalRules) > 0 {
|
||||
errors.LogWarning(context.Background(), `The "finalRules" setting is ignored when "sockopt.dialerProxy" is set, since freedom is not the final outbound.`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
h.finalRules = make([]*FinalRule, 0, len(config.FinalRules))
|
||||
for _, rc := range config.FinalRules {
|
||||
rule, err := buildFinalRule(rc)
|
||||
@@ -253,7 +255,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
ob.Name = "freedom"
|
||||
ob.CanSpliceCopy = 1
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
defaultRule := getDefaultFinalRule(inbound)
|
||||
var defaultRule *FinalRule
|
||||
if !h.usesDialerProxy { // freedom is not the final outbound, final rules do not apply (and the domain is not resolved)
|
||||
defaultRule = getDefaultFinalRule(inbound)
|
||||
}
|
||||
|
||||
destination := ob.Target
|
||||
origTargetAddr := ob.OriginalTarget.Address
|
||||
@@ -342,15 +347,11 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
return h.blackhole(ctx, input, output, blockedRule, blockedDest)
|
||||
}
|
||||
if destination.Address.Family().IsDomain() && (defaultRule != nil || len(h.finalRules) > 0) {
|
||||
if h.usesDialerProxy {
|
||||
errors.LogInfo(ctx, "skipping final rule check for proxied remote endpoint, original target: ", destination)
|
||||
} else {
|
||||
// pre-check may fail or dialer may select another IP
|
||||
remoteDest := net.DestinationFromAddr(conn.RemoteAddr())
|
||||
if rule := h.matchFinalRule(remoteDest.Network, remoteDest.Address, remoteDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
||||
conn.Close()
|
||||
return h.blackhole(ctx, input, output, rule, &remoteDest)
|
||||
}
|
||||
// pre-check may fail or dialer may select another IP
|
||||
remoteDest := net.DestinationFromAddr(conn.RemoteAddr())
|
||||
if rule := h.matchFinalRule(remoteDest.Network, remoteDest.Address, remoteDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
||||
conn.Close()
|
||||
return h.blackhole(ctx, input, output, rule, &remoteDest)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package shadowsocks_2022
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
)
|
||||
|
||||
// MemoryAccount is an account type converted from Account.
|
||||
type MemoryAccount struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
// AsAccount implements protocol.AsAccount.
|
||||
func (u *Account) AsAccount() (protocol.Account, error) {
|
||||
return &MemoryAccount{
|
||||
Key: u.GetKey(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Equals implements protocol.Account.Equals().
|
||||
func (a *MemoryAccount) Equals(another protocol.Account) bool {
|
||||
if account, ok := another.(*MemoryAccount); ok {
|
||||
return a.Key == account.Key
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *MemoryAccount) ToProto() proto.Message {
|
||||
return &Account{
|
||||
Key: a.Key,
|
||||
}
|
||||
}
|
||||
@@ -1,523 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: proxy/shadowsocks_2022/config.proto
|
||||
|
||||
package shadowsocks_2022
|
||||
|
||||
import (
|
||||
net "github.com/xtls/xray-core/common/net"
|
||||
protocol "github.com/xtls/xray-core/common/protocol"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
|
||||
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
|
||||
Level int32 `protobuf:"varint,4,opt,name=level,proto3" json:"level,omitempty"`
|
||||
Network []net.Network `protobuf:"varint,5,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ServerConfig) Reset() {
|
||||
*x = ServerConfig{}
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ServerConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ServerConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ServerConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead.
|
||||
func (*ServerConfig) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_shadowsocks_2022_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ServerConfig) GetMethod() string {
|
||||
if x != nil {
|
||||
return x.Method
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ServerConfig) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ServerConfig) GetEmail() string {
|
||||
if x != nil {
|
||||
return x.Email
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ServerConfig) GetLevel() int32 {
|
||||
if x != nil {
|
||||
return x.Level
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ServerConfig) GetNetwork() []net.Network {
|
||||
if x != nil {
|
||||
return x.Network
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MultiUserServerConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
|
||||
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Users []*protocol.User `protobuf:"bytes,3,rep,name=users,proto3" json:"users,omitempty"`
|
||||
Network []net.Network `protobuf:"varint,4,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *MultiUserServerConfig) Reset() {
|
||||
*x = MultiUserServerConfig{}
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *MultiUserServerConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*MultiUserServerConfig) ProtoMessage() {}
|
||||
|
||||
func (x *MultiUserServerConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use MultiUserServerConfig.ProtoReflect.Descriptor instead.
|
||||
func (*MultiUserServerConfig) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_shadowsocks_2022_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *MultiUserServerConfig) GetMethod() string {
|
||||
if x != nil {
|
||||
return x.Method
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *MultiUserServerConfig) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *MultiUserServerConfig) GetUsers() []*protocol.User {
|
||||
if x != nil {
|
||||
return x.Users
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *MultiUserServerConfig) GetNetwork() []net.Network {
|
||||
if x != nil {
|
||||
return x.Network
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RelayDestination struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Address *net.IPOrDomain `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Port uint32 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"`
|
||||
Email string `protobuf:"bytes,4,opt,name=email,proto3" json:"email,omitempty"`
|
||||
Level int32 `protobuf:"varint,5,opt,name=level,proto3" json:"level,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RelayDestination) Reset() {
|
||||
*x = RelayDestination{}
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RelayDestination) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RelayDestination) ProtoMessage() {}
|
||||
|
||||
func (x *RelayDestination) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RelayDestination.ProtoReflect.Descriptor instead.
|
||||
func (*RelayDestination) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_shadowsocks_2022_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *RelayDestination) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RelayDestination) GetAddress() *net.IPOrDomain {
|
||||
if x != nil {
|
||||
return x.Address
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RelayDestination) GetPort() uint32 {
|
||||
if x != nil {
|
||||
return x.Port
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RelayDestination) GetEmail() string {
|
||||
if x != nil {
|
||||
return x.Email
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RelayDestination) GetLevel() int32 {
|
||||
if x != nil {
|
||||
return x.Level
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type RelayServerConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
|
||||
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Destinations []*RelayDestination `protobuf:"bytes,3,rep,name=destinations,proto3" json:"destinations,omitempty"`
|
||||
Network []net.Network `protobuf:"varint,4,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RelayServerConfig) Reset() {
|
||||
*x = RelayServerConfig{}
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RelayServerConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RelayServerConfig) ProtoMessage() {}
|
||||
|
||||
func (x *RelayServerConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RelayServerConfig.ProtoReflect.Descriptor instead.
|
||||
func (*RelayServerConfig) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_shadowsocks_2022_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *RelayServerConfig) GetMethod() string {
|
||||
if x != nil {
|
||||
return x.Method
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RelayServerConfig) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RelayServerConfig) GetDestinations() []*RelayDestination {
|
||||
if x != nil {
|
||||
return x.Destinations
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RelayServerConfig) GetNetwork() []net.Network {
|
||||
if x != nil {
|
||||
return x.Network
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Account) Reset() {
|
||||
*x = Account{}
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Account) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Account) ProtoMessage() {}
|
||||
|
||||
func (x *Account) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Account.ProtoReflect.Descriptor instead.
|
||||
func (*Account) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_shadowsocks_2022_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *Account) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ClientConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
|
||||
Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"`
|
||||
Key string `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClientConfig) Reset() {
|
||||
*x = ClientConfig{}
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ClientConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ClientConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead.
|
||||
func (*ClientConfig) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_shadowsocks_2022_config_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetAddress() *net.IPOrDomain {
|
||||
if x != nil {
|
||||
return x.Address
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetPort() uint32 {
|
||||
if x != nil {
|
||||
return x.Port
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetMethod() string {
|
||||
if x != nil {
|
||||
return x.Method
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_proxy_shadowsocks_2022_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_proxy_shadowsocks_2022_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"#proxy/shadowsocks_2022/config.proto\x12\x1bxray.proxy.shadowsocks_2022\x1a\x18common/net/network.proto\x1a\x18common/net/address.proto\x1a\x1acommon/protocol/user.proto\"\x98\x01\n" +
|
||||
"\fServerConfig\x12\x16\n" +
|
||||
"\x06method\x18\x01 \x01(\tR\x06method\x12\x10\n" +
|
||||
"\x03key\x18\x02 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05email\x18\x03 \x01(\tR\x05email\x12\x14\n" +
|
||||
"\x05level\x18\x04 \x01(\x05R\x05level\x122\n" +
|
||||
"\anetwork\x18\x05 \x03(\x0e2\x18.xray.common.net.NetworkR\anetwork\"\xa7\x01\n" +
|
||||
"\x15MultiUserServerConfig\x12\x16\n" +
|
||||
"\x06method\x18\x01 \x01(\tR\x06method\x12\x10\n" +
|
||||
"\x03key\x18\x02 \x01(\tR\x03key\x120\n" +
|
||||
"\x05users\x18\x03 \x03(\v2\x1a.xray.common.protocol.UserR\x05users\x122\n" +
|
||||
"\anetwork\x18\x04 \x03(\x0e2\x18.xray.common.net.NetworkR\anetwork\"\x9b\x01\n" +
|
||||
"\x10RelayDestination\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x125\n" +
|
||||
"\aaddress\x18\x02 \x01(\v2\x1b.xray.common.net.IPOrDomainR\aaddress\x12\x12\n" +
|
||||
"\x04port\x18\x03 \x01(\rR\x04port\x12\x14\n" +
|
||||
"\x05email\x18\x04 \x01(\tR\x05email\x12\x14\n" +
|
||||
"\x05level\x18\x05 \x01(\x05R\x05level\"\xc4\x01\n" +
|
||||
"\x11RelayServerConfig\x12\x16\n" +
|
||||
"\x06method\x18\x01 \x01(\tR\x06method\x12\x10\n" +
|
||||
"\x03key\x18\x02 \x01(\tR\x03key\x12Q\n" +
|
||||
"\fdestinations\x18\x03 \x03(\v2-.xray.proxy.shadowsocks_2022.RelayDestinationR\fdestinations\x122\n" +
|
||||
"\anetwork\x18\x04 \x03(\x0e2\x18.xray.common.net.NetworkR\anetwork\"\x1b\n" +
|
||||
"\aAccount\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\"\x83\x01\n" +
|
||||
"\fClientConfig\x125\n" +
|
||||
"\aaddress\x18\x01 \x01(\v2\x1b.xray.common.net.IPOrDomainR\aaddress\x12\x12\n" +
|
||||
"\x04port\x18\x02 \x01(\rR\x04port\x12\x16\n" +
|
||||
"\x06method\x18\x03 \x01(\tR\x06method\x12\x10\n" +
|
||||
"\x03key\x18\x04 \x01(\tR\x03keyBr\n" +
|
||||
"\x1fcom.xray.proxy.shadowsocks_2022P\x01Z0github.com/xtls/xray-core/proxy/shadowsocks_2022\xaa\x02\x1aXray.Proxy.Shadowsocks2022b\x06proto3"
|
||||
|
||||
var (
|
||||
file_proxy_shadowsocks_2022_config_proto_rawDescOnce sync.Once
|
||||
file_proxy_shadowsocks_2022_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_proxy_shadowsocks_2022_config_proto_rawDescGZIP() []byte {
|
||||
file_proxy_shadowsocks_2022_config_proto_rawDescOnce.Do(func() {
|
||||
file_proxy_shadowsocks_2022_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_shadowsocks_2022_config_proto_rawDesc), len(file_proxy_shadowsocks_2022_config_proto_rawDesc)))
|
||||
})
|
||||
return file_proxy_shadowsocks_2022_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proxy_shadowsocks_2022_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_proxy_shadowsocks_2022_config_proto_goTypes = []any{
|
||||
(*ServerConfig)(nil), // 0: xray.proxy.shadowsocks_2022.ServerConfig
|
||||
(*MultiUserServerConfig)(nil), // 1: xray.proxy.shadowsocks_2022.MultiUserServerConfig
|
||||
(*RelayDestination)(nil), // 2: xray.proxy.shadowsocks_2022.RelayDestination
|
||||
(*RelayServerConfig)(nil), // 3: xray.proxy.shadowsocks_2022.RelayServerConfig
|
||||
(*Account)(nil), // 4: xray.proxy.shadowsocks_2022.Account
|
||||
(*ClientConfig)(nil), // 5: xray.proxy.shadowsocks_2022.ClientConfig
|
||||
(net.Network)(0), // 6: xray.common.net.Network
|
||||
(*protocol.User)(nil), // 7: xray.common.protocol.User
|
||||
(*net.IPOrDomain)(nil), // 8: xray.common.net.IPOrDomain
|
||||
}
|
||||
var file_proxy_shadowsocks_2022_config_proto_depIdxs = []int32{
|
||||
6, // 0: xray.proxy.shadowsocks_2022.ServerConfig.network:type_name -> xray.common.net.Network
|
||||
7, // 1: xray.proxy.shadowsocks_2022.MultiUserServerConfig.users:type_name -> xray.common.protocol.User
|
||||
6, // 2: xray.proxy.shadowsocks_2022.MultiUserServerConfig.network:type_name -> xray.common.net.Network
|
||||
8, // 3: xray.proxy.shadowsocks_2022.RelayDestination.address:type_name -> xray.common.net.IPOrDomain
|
||||
2, // 4: xray.proxy.shadowsocks_2022.RelayServerConfig.destinations:type_name -> xray.proxy.shadowsocks_2022.RelayDestination
|
||||
6, // 5: xray.proxy.shadowsocks_2022.RelayServerConfig.network:type_name -> xray.common.net.Network
|
||||
8, // 6: xray.proxy.shadowsocks_2022.ClientConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||
7, // [7:7] is the sub-list for method output_type
|
||||
7, // [7:7] is the sub-list for method input_type
|
||||
7, // [7:7] is the sub-list for extension type_name
|
||||
7, // [7:7] is the sub-list for extension extendee
|
||||
0, // [0:7] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proxy_shadowsocks_2022_config_proto_init() }
|
||||
func file_proxy_shadowsocks_2022_config_proto_init() {
|
||||
if File_proxy_shadowsocks_2022_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_shadowsocks_2022_config_proto_rawDesc), len(file_proxy_shadowsocks_2022_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_proxy_shadowsocks_2022_config_proto_goTypes,
|
||||
DependencyIndexes: file_proxy_shadowsocks_2022_config_proto_depIdxs,
|
||||
MessageInfos: file_proxy_shadowsocks_2022_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proxy_shadowsocks_2022_config_proto = out.File
|
||||
file_proxy_shadowsocks_2022_config_proto_goTypes = nil
|
||||
file_proxy_shadowsocks_2022_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.proxy.shadowsocks_2022;
|
||||
option csharp_namespace = "Xray.Proxy.Shadowsocks2022";
|
||||
option go_package = "github.com/xtls/xray-core/proxy/shadowsocks_2022";
|
||||
option java_package = "com.xray.proxy.shadowsocks_2022";
|
||||
option java_multiple_files = true;
|
||||
|
||||
import "common/net/network.proto";
|
||||
import "common/net/address.proto";
|
||||
import "common/protocol/user.proto";
|
||||
|
||||
message ServerConfig {
|
||||
string method = 1;
|
||||
string key = 2;
|
||||
string email = 3;
|
||||
int32 level = 4;
|
||||
repeated xray.common.net.Network network = 5;
|
||||
}
|
||||
|
||||
message MultiUserServerConfig {
|
||||
string method = 1;
|
||||
string key = 2;
|
||||
repeated xray.common.protocol.User users = 3;
|
||||
repeated xray.common.net.Network network = 4;
|
||||
}
|
||||
|
||||
message RelayDestination {
|
||||
string key = 1;
|
||||
xray.common.net.IPOrDomain address = 2;
|
||||
uint32 port = 3;
|
||||
string email = 4;
|
||||
int32 level = 5;
|
||||
}
|
||||
|
||||
message RelayServerConfig {
|
||||
string method = 1;
|
||||
string key = 2;
|
||||
repeated RelayDestination destinations = 3;
|
||||
repeated xray.common.net.Network network = 4;
|
||||
}
|
||||
|
||||
message Account {
|
||||
string key = 1;
|
||||
}
|
||||
|
||||
message ClientConfig {
|
||||
xray.common.net.IPOrDomain address = 1;
|
||||
uint32 port = 2;
|
||||
string method = 3;
|
||||
string key = 4;
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
package shadowsocks_2022
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
shadowsocks "github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
C "github.com/sagernet/sing/common"
|
||||
B "github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/common/singbridge"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return NewServer(ctx, config.(*ServerConfig))
|
||||
}))
|
||||
}
|
||||
|
||||
type Inbound struct {
|
||||
networks []net.Network
|
||||
service shadowsocks.Service
|
||||
email string
|
||||
level int
|
||||
}
|
||||
|
||||
func NewServer(ctx context.Context, config *ServerConfig) (*Inbound, error) {
|
||||
networks := config.Network
|
||||
if len(networks) == 0 {
|
||||
networks = []net.Network{
|
||||
net.Network_TCP,
|
||||
net.Network_UDP,
|
||||
}
|
||||
}
|
||||
inbound := &Inbound{
|
||||
networks: networks,
|
||||
email: config.Email,
|
||||
level: int(config.Level),
|
||||
}
|
||||
if !C.Contains(shadowaead_2022.List, config.Method) {
|
||||
return nil, errors.New("unsupported method ", config.Method)
|
||||
}
|
||||
service, err := shadowaead_2022.NewServiceWithPassword(config.Method, config.Key, 500, inbound, nil)
|
||||
if err != nil {
|
||||
return nil, errors.New("create service").Base(err)
|
||||
}
|
||||
inbound.service = service
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (i *Inbound) Network() []net.Network {
|
||||
return i.networks
|
||||
}
|
||||
|
||||
func (i *Inbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
inbound.Name = "shadowsocks-2022"
|
||||
inbound.CanSpliceCopy = 3
|
||||
|
||||
var metadata M.Metadata
|
||||
if inbound.Source.IsValid() {
|
||||
metadata.Source = M.ParseSocksaddr(inbound.Source.NetAddr())
|
||||
}
|
||||
|
||||
ctx = session.ContextWithDispatcher(ctx, dispatcher)
|
||||
|
||||
if network == net.Network_TCP {
|
||||
return singbridge.ReturnError(i.service.NewConnection(ctx, connection, metadata))
|
||||
} else {
|
||||
reader := buf.NewReader(connection)
|
||||
pc := &natPacketConn{connection}
|
||||
for {
|
||||
mb, err := reader.ReadMultiBuffer()
|
||||
if err != nil {
|
||||
buf.ReleaseMulti(mb)
|
||||
return singbridge.ReturnError(err)
|
||||
}
|
||||
for _, buffer := range mb {
|
||||
packet := B.As(buffer.Bytes()).ToOwned()
|
||||
buffer.Release()
|
||||
err = i.service.NewPacket(ctx, pc, packet, metadata)
|
||||
if err != nil {
|
||||
packet.Release()
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
inbound.User = &protocol.MemoryUser{
|
||||
Email: i.email,
|
||||
Level: uint32(i.level),
|
||||
}
|
||||
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||
From: metadata.Source,
|
||||
To: metadata.Destination,
|
||||
Status: log.AccessAccepted,
|
||||
Email: i.email,
|
||||
})
|
||||
errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination)
|
||||
dispatcher := session.DispatcherFromContext(ctx)
|
||||
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_TCP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
link, err := dispatcher.Dispatch(ctx, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return singbridge.CopyConn(ctx, nil, link, conn)
|
||||
}
|
||||
|
||||
func (i *Inbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
inbound.User = &protocol.MemoryUser{
|
||||
Email: i.email,
|
||||
Level: uint32(i.level),
|
||||
}
|
||||
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||
From: metadata.Source,
|
||||
To: metadata.Destination,
|
||||
Status: log.AccessAccepted,
|
||||
Email: i.email,
|
||||
})
|
||||
errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination)
|
||||
dispatcher := session.DispatcherFromContext(ctx)
|
||||
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
link, err := dispatcher.Dispatch(ctx, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outConn := &singbridge.PacketConnWrapper{
|
||||
Reader: link.Reader,
|
||||
Writer: link.Writer,
|
||||
Dest: destination,
|
||||
T: signal.CancelAfterInactivity(ctx, func() {
|
||||
common.Interrupt(link.Reader)
|
||||
}, 300*time.Second),
|
||||
}
|
||||
return bufio.CopyPacketConn(ctx, conn, outConn)
|
||||
}
|
||||
|
||||
func (i *Inbound) NewError(ctx context.Context, err error) {
|
||||
if E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
errors.LogWarning(ctx, err.Error())
|
||||
}
|
||||
|
||||
type natPacketConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *natPacketConn) ReadPacket(buffer *B.Buffer) (addr M.Socksaddr, err error) {
|
||||
_, err = buffer.ReadFrom(c)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *natPacketConn) WritePacket(buffer *B.Buffer, addr M.Socksaddr) error {
|
||||
_, err := buffer.WriteTo(c)
|
||||
return err
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
package shadowsocks_2022
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
C "github.com/sagernet/sing/common"
|
||||
A "github.com/sagernet/sing/common/auth"
|
||||
B "github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/common/singbridge"
|
||||
"github.com/xtls/xray-core/common/uuid"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*MultiUserServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return NewMultiServer(ctx, config.(*MultiUserServerConfig))
|
||||
}))
|
||||
}
|
||||
|
||||
type MultiUserInbound struct {
|
||||
sync.Mutex
|
||||
networks []net.Network
|
||||
users []*protocol.MemoryUser
|
||||
service *shadowaead_2022.MultiService[int]
|
||||
}
|
||||
|
||||
func NewMultiServer(ctx context.Context, config *MultiUserServerConfig) (*MultiUserInbound, error) {
|
||||
networks := config.Network
|
||||
if len(networks) == 0 {
|
||||
networks = []net.Network{
|
||||
net.Network_TCP,
|
||||
net.Network_UDP,
|
||||
}
|
||||
}
|
||||
memUsers := []*protocol.MemoryUser{}
|
||||
for i, user := range config.Users {
|
||||
if user.Email == "" {
|
||||
u := uuid.New()
|
||||
user.Email = "unnamed-user-" + strconv.Itoa(i) + "-" + u.String()
|
||||
}
|
||||
u, err := user.ToMemoryUser()
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to get shadowsocks user").Base(err).AtError()
|
||||
}
|
||||
memUsers = append(memUsers, u)
|
||||
}
|
||||
|
||||
inbound := &MultiUserInbound{
|
||||
networks: networks,
|
||||
users: memUsers,
|
||||
}
|
||||
if config.Key == "" {
|
||||
return nil, errors.New("missing key")
|
||||
}
|
||||
psk, err := base64.StdEncoding.DecodeString(config.Key)
|
||||
if err != nil {
|
||||
return nil, errors.New("parse config").Base(err)
|
||||
}
|
||||
service, err := shadowaead_2022.NewMultiService[int](config.Method, psk, 500, inbound, nil)
|
||||
if err != nil {
|
||||
return nil, errors.New("create service").Base(err)
|
||||
}
|
||||
err = service.UpdateUsersWithPasswords(
|
||||
C.MapIndexed(memUsers, func(index int, it *protocol.MemoryUser) int { return index }),
|
||||
C.Map(memUsers, func(it *protocol.MemoryUser) string { return it.Account.(*MemoryAccount).Key }),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.New("create service").Base(err)
|
||||
}
|
||||
|
||||
inbound.service = service
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
// AddUser implements proxy.UserManager.AddUser().
|
||||
func (i *MultiUserInbound) AddUser(ctx context.Context, u *protocol.MemoryUser) error {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
if u.Email != "" {
|
||||
for idx := range i.users {
|
||||
if i.users[idx].Email == u.Email {
|
||||
return errors.New("User ", u.Email, " already exists.")
|
||||
}
|
||||
}
|
||||
}
|
||||
i.users = append(i.users, u)
|
||||
|
||||
// sync to multi service
|
||||
// Considering implements shadowsocks2022 in xray-core may have better performance.
|
||||
i.service.UpdateUsersWithPasswords(
|
||||
C.MapIndexed(i.users, func(index int, it *protocol.MemoryUser) int { return index }),
|
||||
C.Map(i.users, func(it *protocol.MemoryUser) string { return it.Account.(*MemoryAccount).Key }),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveUser implements proxy.UserManager.RemoveUser().
|
||||
func (i *MultiUserInbound) RemoveUser(ctx context.Context, email string) error {
|
||||
if email == "" {
|
||||
return errors.New("Email must not be empty.")
|
||||
}
|
||||
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
idx := -1
|
||||
for ii, u := range i.users {
|
||||
if strings.EqualFold(u.Email, email) {
|
||||
idx = ii
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if idx == -1 {
|
||||
return errors.New("User ", email, " not found.")
|
||||
}
|
||||
|
||||
ulen := len(i.users)
|
||||
|
||||
i.users[idx] = i.users[ulen-1]
|
||||
i.users[ulen-1] = nil
|
||||
i.users = i.users[:ulen-1]
|
||||
|
||||
// sync to multi service
|
||||
// Considering implements shadowsocks2022 in xray-core may have better performance.
|
||||
i.service.UpdateUsersWithPasswords(
|
||||
C.MapIndexed(i.users, func(index int, it *protocol.MemoryUser) int { return index }),
|
||||
C.Map(i.users, func(it *protocol.MemoryUser) string { return it.Account.(*MemoryAccount).Key }),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUser implements proxy.UserManager.GetUser().
|
||||
func (i *MultiUserInbound) GetUser(ctx context.Context, email string) *protocol.MemoryUser {
|
||||
if email == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
for _, u := range i.users {
|
||||
if strings.EqualFold(u.Email, email) {
|
||||
return u
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUsers implements proxy.UserManager.GetUsers().
|
||||
func (i *MultiUserInbound) GetUsers(ctx context.Context) []*protocol.MemoryUser {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
dst := make([]*protocol.MemoryUser, len(i.users))
|
||||
copy(dst, i.users)
|
||||
return dst
|
||||
}
|
||||
|
||||
// GetUsersCount implements proxy.UserManager.GetUsersCount().
|
||||
func (i *MultiUserInbound) GetUsersCount(context.Context) int64 {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
return int64(len(i.users))
|
||||
}
|
||||
|
||||
func (i *MultiUserInbound) Network() []net.Network {
|
||||
return i.networks
|
||||
}
|
||||
|
||||
func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
inbound.Name = "shadowsocks-2022-multi"
|
||||
inbound.CanSpliceCopy = 3
|
||||
|
||||
var metadata M.Metadata
|
||||
if inbound.Source.IsValid() {
|
||||
metadata.Source = M.ParseSocksaddr(inbound.Source.NetAddr())
|
||||
}
|
||||
|
||||
ctx = session.ContextWithDispatcher(ctx, dispatcher)
|
||||
|
||||
if network == net.Network_TCP {
|
||||
return singbridge.ReturnError(i.service.NewConnection(ctx, connection, metadata))
|
||||
} else {
|
||||
reader := buf.NewReader(connection)
|
||||
pc := &natPacketConn{connection}
|
||||
for {
|
||||
mb, err := reader.ReadMultiBuffer()
|
||||
if err != nil {
|
||||
buf.ReleaseMulti(mb)
|
||||
return singbridge.ReturnError(err)
|
||||
}
|
||||
for _, buffer := range mb {
|
||||
packet := B.As(buffer.Bytes()).ToOwned()
|
||||
buffer.Release()
|
||||
err = i.service.NewPacket(ctx, pc, packet, metadata)
|
||||
if err != nil {
|
||||
packet.Release()
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *MultiUserInbound) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
userInt, _ := A.UserFromContext[int](ctx)
|
||||
user := i.users[userInt]
|
||||
inbound.User = user
|
||||
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||
From: metadata.Source,
|
||||
To: metadata.Destination,
|
||||
Status: log.AccessAccepted,
|
||||
Email: user.Email,
|
||||
})
|
||||
errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination)
|
||||
dispatcher := session.DispatcherFromContext(ctx)
|
||||
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_TCP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
link, err := dispatcher.Dispatch(ctx, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return singbridge.CopyConn(ctx, conn, link, conn)
|
||||
}
|
||||
|
||||
func (i *MultiUserInbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
userInt, _ := A.UserFromContext[int](ctx)
|
||||
user := i.users[userInt]
|
||||
inbound.User = user
|
||||
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||
From: metadata.Source,
|
||||
To: metadata.Destination,
|
||||
Status: log.AccessAccepted,
|
||||
Email: user.Email,
|
||||
})
|
||||
errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination)
|
||||
dispatcher := session.DispatcherFromContext(ctx)
|
||||
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
link, err := dispatcher.Dispatch(ctx, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outConn := &singbridge.PacketConnWrapper{
|
||||
Reader: link.Reader,
|
||||
Writer: link.Writer,
|
||||
Dest: destination,
|
||||
T: signal.CancelAfterInactivity(ctx, func() {
|
||||
common.Interrupt(link.Reader)
|
||||
}, 300*time.Second),
|
||||
}
|
||||
return bufio.CopyPacketConn(ctx, conn, outConn)
|
||||
}
|
||||
|
||||
func (i *MultiUserInbound) NewError(ctx context.Context, err error) {
|
||||
if E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
errors.LogWarning(ctx, err.Error())
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
package shadowsocks_2022
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
C "github.com/sagernet/sing/common"
|
||||
A "github.com/sagernet/sing/common/auth"
|
||||
B "github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/common/singbridge"
|
||||
"github.com/xtls/xray-core/common/uuid"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*RelayServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return NewRelayServer(ctx, config.(*RelayServerConfig))
|
||||
}))
|
||||
}
|
||||
|
||||
type RelayInbound struct {
|
||||
networks []net.Network
|
||||
destinations []*RelayDestination
|
||||
service *shadowaead_2022.RelayService[int]
|
||||
}
|
||||
|
||||
func NewRelayServer(ctx context.Context, config *RelayServerConfig) (*RelayInbound, error) {
|
||||
networks := config.Network
|
||||
if len(networks) == 0 {
|
||||
networks = []net.Network{
|
||||
net.Network_TCP,
|
||||
net.Network_UDP,
|
||||
}
|
||||
}
|
||||
inbound := &RelayInbound{
|
||||
networks: networks,
|
||||
destinations: config.Destinations,
|
||||
}
|
||||
if !C.Contains(shadowaead_2022.List, config.Method) || !strings.Contains(config.Method, "aes") {
|
||||
return nil, errors.New("unsupported method ", config.Method)
|
||||
}
|
||||
service, err := shadowaead_2022.NewRelayServiceWithPassword[int](config.Method, config.Key, 500, inbound)
|
||||
if err != nil {
|
||||
return nil, errors.New("create service").Base(err)
|
||||
}
|
||||
|
||||
for i, destination := range config.Destinations {
|
||||
if destination.Email == "" {
|
||||
u := uuid.New()
|
||||
destination.Email = "unnamed-destination-" + strconv.Itoa(i) + "-" + u.String()
|
||||
}
|
||||
}
|
||||
err = service.UpdateUsersWithPasswords(
|
||||
C.MapIndexed(config.Destinations, func(index int, it *RelayDestination) int { return index }),
|
||||
C.Map(config.Destinations, func(it *RelayDestination) string { return it.Key }),
|
||||
C.Map(config.Destinations, func(it *RelayDestination) M.Socksaddr {
|
||||
return singbridge.ToSocksaddr(net.Destination{
|
||||
Address: it.Address.AsAddress(),
|
||||
Port: net.Port(it.Port),
|
||||
})
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.New("create service").Base(err)
|
||||
}
|
||||
inbound.service = service
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (i *RelayInbound) Network() []net.Network {
|
||||
return i.networks
|
||||
}
|
||||
|
||||
func (i *RelayInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
inbound.Name = "shadowsocks-2022-relay"
|
||||
inbound.CanSpliceCopy = 3
|
||||
|
||||
var metadata M.Metadata
|
||||
if inbound.Source.IsValid() {
|
||||
metadata.Source = M.ParseSocksaddr(inbound.Source.NetAddr())
|
||||
}
|
||||
|
||||
ctx = session.ContextWithDispatcher(ctx, dispatcher)
|
||||
|
||||
if network == net.Network_TCP {
|
||||
return singbridge.ReturnError(i.service.NewConnection(ctx, connection, metadata))
|
||||
} else {
|
||||
reader := buf.NewReader(connection)
|
||||
pc := &natPacketConn{connection}
|
||||
for {
|
||||
mb, err := reader.ReadMultiBuffer()
|
||||
if err != nil {
|
||||
buf.ReleaseMulti(mb)
|
||||
return singbridge.ReturnError(err)
|
||||
}
|
||||
for _, buffer := range mb {
|
||||
packet := B.As(buffer.Bytes()).ToOwned()
|
||||
buffer.Release()
|
||||
err = i.service.NewPacket(ctx, pc, packet, metadata)
|
||||
if err != nil {
|
||||
packet.Release()
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *RelayInbound) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
userInt, _ := A.UserFromContext[int](ctx)
|
||||
user := i.destinations[userInt]
|
||||
inbound.User = &protocol.MemoryUser{
|
||||
Email: user.Email,
|
||||
Level: uint32(user.Level),
|
||||
}
|
||||
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||
From: metadata.Source,
|
||||
To: metadata.Destination,
|
||||
Status: log.AccessAccepted,
|
||||
Email: user.Email,
|
||||
})
|
||||
errors.LogInfo(ctx, "tunnelling request to tcp:", metadata.Destination)
|
||||
dispatcher := session.DispatcherFromContext(ctx)
|
||||
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_TCP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
link, err := dispatcher.Dispatch(ctx, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return singbridge.CopyConn(ctx, nil, link, conn)
|
||||
}
|
||||
|
||||
func (i *RelayInbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
userInt, _ := A.UserFromContext[int](ctx)
|
||||
user := i.destinations[userInt]
|
||||
inbound.User = &protocol.MemoryUser{
|
||||
Email: user.Email,
|
||||
Level: uint32(user.Level),
|
||||
}
|
||||
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||
From: metadata.Source,
|
||||
To: metadata.Destination,
|
||||
Status: log.AccessAccepted,
|
||||
Email: user.Email,
|
||||
})
|
||||
errors.LogInfo(ctx, "tunnelling request to udp:", metadata.Destination)
|
||||
dispatcher := session.DispatcherFromContext(ctx)
|
||||
destination, err := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
link, err := dispatcher.Dispatch(ctx, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outConn := &singbridge.PacketConnWrapper{
|
||||
Reader: link.Reader,
|
||||
Writer: link.Writer,
|
||||
Dest: destination,
|
||||
T: signal.CancelAfterInactivity(ctx, func() {
|
||||
common.Interrupt(link.Reader)
|
||||
}, 300*time.Second),
|
||||
}
|
||||
return bufio.CopyPacketConn(ctx, conn, outConn)
|
||||
}
|
||||
|
||||
func (i *RelayInbound) NewError(ctx context.Context, err error) {
|
||||
if E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
errors.LogWarning(ctx, err.Error())
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package shadowsocks_2022
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
shadowsocks "github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
C "github.com/sagernet/sing/common"
|
||||
B "github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/common/singbridge"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return NewClient(ctx, config.(*ClientConfig))
|
||||
}))
|
||||
}
|
||||
|
||||
type Outbound struct {
|
||||
ctx context.Context
|
||||
server net.Destination
|
||||
method shadowsocks.Method
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, config *ClientConfig) (*Outbound, error) {
|
||||
o := &Outbound{
|
||||
ctx: ctx,
|
||||
server: net.Destination{
|
||||
Address: config.Address.AsAddress(),
|
||||
Port: net.Port(config.Port),
|
||||
Network: net.Network_TCP,
|
||||
},
|
||||
}
|
||||
if C.Contains(shadowaead_2022.List, config.Method) {
|
||||
if config.Key == "" {
|
||||
return nil, errors.New("missing psk")
|
||||
}
|
||||
method, err := shadowaead_2022.NewWithPassword(config.Method, config.Key, nil)
|
||||
if err != nil {
|
||||
return nil, errors.New("create method").Base(err)
|
||||
}
|
||||
o.method = method
|
||||
} else {
|
||||
return nil, errors.New("unknown method ", config.Method)
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||
var inboundConn net.Conn
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
if inbound != nil {
|
||||
inboundConn = inbound.Conn
|
||||
}
|
||||
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
if !ob.Target.IsValid() {
|
||||
return errors.New("target not specified")
|
||||
}
|
||||
ob.Name = "shadowsocks-2022"
|
||||
ob.CanSpliceCopy = 3
|
||||
destination := ob.Target
|
||||
network := destination.Network
|
||||
|
||||
errors.LogInfo(ctx, "tunneling request to ", destination, " via ", o.server.NetAddr())
|
||||
|
||||
serverDestination := o.server
|
||||
serverDestination.Network = network
|
||||
connection, err := dialer.Dial(ctx, serverDestination)
|
||||
if err != nil {
|
||||
return errors.New("failed to connect to server").Base(err)
|
||||
}
|
||||
defer connection.Close()
|
||||
|
||||
if session.TimeoutOnlyFromContext(ctx) {
|
||||
ctx, _ = context.WithCancel(context.Background())
|
||||
}
|
||||
|
||||
if network == net.Network_TCP {
|
||||
serverConn := o.method.DialEarlyConn(connection, singbridge.ToSocksaddr(destination))
|
||||
var handshake bool
|
||||
if timeoutReader, isTimeoutReader := link.Reader.(buf.TimeoutReader); isTimeoutReader {
|
||||
mb, err := timeoutReader.ReadMultiBufferTimeout(time.Millisecond * 100)
|
||||
if err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
|
||||
return errors.New("read payload").Base(err)
|
||||
}
|
||||
payload := B.New()
|
||||
for {
|
||||
payload.Reset()
|
||||
nb, n := buf.SplitBytes(mb, payload.FreeBytes())
|
||||
if n > 0 {
|
||||
payload.Truncate(n)
|
||||
_, err = serverConn.Write(payload.Bytes())
|
||||
if err != nil {
|
||||
payload.Release()
|
||||
return errors.New("write payload").Base(err)
|
||||
}
|
||||
handshake = true
|
||||
}
|
||||
if nb.IsEmpty() {
|
||||
break
|
||||
}
|
||||
mb = nb
|
||||
}
|
||||
payload.Release()
|
||||
}
|
||||
if !handshake {
|
||||
_, err = serverConn.Write(nil)
|
||||
if err != nil {
|
||||
return errors.New("client handshake").Base(err)
|
||||
}
|
||||
}
|
||||
return singbridge.CopyConn(ctx, inboundConn, link, serverConn)
|
||||
} else {
|
||||
var packetConn N.PacketConn
|
||||
if pc, isPacketConn := inboundConn.(N.PacketConn); isPacketConn {
|
||||
packetConn = pc
|
||||
} else if nc, isNetPacket := inboundConn.(net.PacketConn); isNetPacket {
|
||||
packetConn = bufio.NewPacketConn(nc)
|
||||
} else {
|
||||
packetConn = &singbridge.PacketConnWrapper{
|
||||
Reader: link.Reader,
|
||||
Writer: link.Writer,
|
||||
Conn: inboundConn,
|
||||
Dest: destination,
|
||||
T: signal.CancelAfterInactivity(ctx, func() {
|
||||
common.Interrupt(link.Reader)
|
||||
}, 300*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
serverConn := o.method.DialPacketConn(connection)
|
||||
return singbridge.ReturnError(bufio.CopyPacketConn(ctx, packetConn, serverConn))
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package shadowsocks_2022
|
||||
@@ -1,221 +0,0 @@
|
||||
package scenarios
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
"github.com/xtls/xray-core/app/log"
|
||||
"github.com/xtls/xray-core/app/proxyman"
|
||||
"github.com/xtls/xray-core/common"
|
||||
clog "github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/proxy/dokodemo"
|
||||
"github.com/xtls/xray-core/proxy/freedom"
|
||||
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
|
||||
"github.com/xtls/xray-core/testing/servers/tcp"
|
||||
"github.com/xtls/xray-core/testing/servers/udp"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func TestShadowsocks2022Tcp(t *testing.T) {
|
||||
for _, method := range shadowaead_2022.List {
|
||||
password := make([]byte, 32)
|
||||
rand.Read(password)
|
||||
t.Run(method, func(t *testing.T) {
|
||||
testShadowsocks2022Tcp(t, method, base64.StdEncoding.EncodeToString(password))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShadowsocks2022UdpAES128(t *testing.T) {
|
||||
password := make([]byte, 32)
|
||||
rand.Read(password)
|
||||
testShadowsocks2022Udp(t, shadowaead_2022.List[0], base64.StdEncoding.EncodeToString(password))
|
||||
}
|
||||
|
||||
func TestShadowsocks2022UdpAES256(t *testing.T) {
|
||||
password := make([]byte, 32)
|
||||
rand.Read(password)
|
||||
testShadowsocks2022Udp(t, shadowaead_2022.List[1], base64.StdEncoding.EncodeToString(password))
|
||||
}
|
||||
|
||||
func TestShadowsocks2022UdpChacha(t *testing.T) {
|
||||
password := make([]byte, 32)
|
||||
rand.Read(password)
|
||||
testShadowsocks2022Udp(t, shadowaead_2022.List[2], base64.StdEncoding.EncodeToString(password))
|
||||
}
|
||||
|
||||
func testShadowsocks2022Tcp(t *testing.T, method string, password string) {
|
||||
tcpServer := tcp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
dest, err := tcpServer.Start()
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
serverPort := tcp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: clog.Severity_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*core.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks_2022.ServerConfig{
|
||||
Method: method,
|
||||
Key: password,
|
||||
Network: []net.Network{net.Network_TCP},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{
|
||||
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
clientPort := tcp.PickPort()
|
||||
clientConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: clog.Severity_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*core.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||
RewriteAddress: net.NewIPOrDomain(dest.Address),
|
||||
RewritePort: uint32(dest.Port),
|
||||
AllowedNetworks: []net.Network{net.Network_TCP},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks_2022.ClientConfig{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
Method: method,
|
||||
Key: password,
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||
common.Must(err)
|
||||
defer CloseAllServers(servers)
|
||||
|
||||
var errGroup errgroup.Group
|
||||
for range 3 {
|
||||
errGroup.Go(testTCPConn(clientPort, 10240*1024, time.Second*20))
|
||||
}
|
||||
|
||||
if err := errGroup.Wait(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testShadowsocks2022Udp(t *testing.T, method string, password string) {
|
||||
udpServer := udp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
udpDest, err := udpServer.Start()
|
||||
common.Must(err)
|
||||
defer udpServer.Close()
|
||||
|
||||
serverPort := udp.PickPort()
|
||||
serverConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: clog.Severity_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*core.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks_2022.ServerConfig{
|
||||
Method: method,
|
||||
Key: password,
|
||||
Network: []net.Network{net.Network_UDP},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{
|
||||
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
udpClientPort := udp.PickPort()
|
||||
clientConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: clog.Severity_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*core.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(udpClientPort)}},
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||
RewriteAddress: net.NewIPOrDomain(udpDest.Address),
|
||||
RewritePort: uint32(udpDest.Port),
|
||||
AllowedNetworks: []net.Network{net.Network_UDP},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks_2022.ClientConfig{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
Method: method,
|
||||
Key: password,
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||
common.Must(err)
|
||||
defer CloseAllServers(servers)
|
||||
|
||||
var errGroup errgroup.Group
|
||||
for range 3 {
|
||||
errGroup.Go(testUDPConn(udpClientPort, 1024, time.Second*5))
|
||||
}
|
||||
|
||||
if err := errGroup.Wait(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
+63
-138
@@ -206,7 +206,7 @@ func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
|
||||
|
||||
// Deprecated: Use SocketConfig_TProxyMode.Descriptor instead.
|
||||
func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{5, 0}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{4, 0}
|
||||
}
|
||||
|
||||
type TransportConfig struct {
|
||||
@@ -382,66 +382,6 @@ func (x *StreamConfig) GetSocketSettings() *SocketConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
type UdpHop struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Ports []uint32 `protobuf:"varint,1,rep,packed,name=ports,proto3" json:"ports,omitempty"`
|
||||
IntervalMin int64 `protobuf:"varint,2,opt,name=interval_min,json=intervalMin,proto3" json:"interval_min,omitempty"`
|
||||
IntervalMax int64 `protobuf:"varint,3,opt,name=interval_max,json=intervalMax,proto3" json:"interval_max,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UdpHop) Reset() {
|
||||
*x = UdpHop{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UdpHop) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UdpHop) ProtoMessage() {}
|
||||
|
||||
func (x *UdpHop) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UdpHop.ProtoReflect.Descriptor instead.
|
||||
func (*UdpHop) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *UdpHop) GetPorts() []uint32 {
|
||||
if x != nil {
|
||||
return x.Ports
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UdpHop) GetIntervalMin() int64 {
|
||||
if x != nil {
|
||||
return x.IntervalMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UdpHop) GetIntervalMax() int64 {
|
||||
if x != nil {
|
||||
return x.IntervalMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type QuicParams struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Congestion string `protobuf:"bytes,1,opt,name=congestion,proto3" json:"congestion,omitempty"`
|
||||
@@ -449,25 +389,24 @@ type QuicParams struct {
|
||||
BrutalUp uint64 `protobuf:"varint,3,opt,name=brutal_up,json=brutalUp,proto3" json:"brutal_up,omitempty"`
|
||||
BrutalDown uint64 `protobuf:"varint,4,opt,name=brutal_down,json=brutalDown,proto3" json:"brutal_down,omitempty"`
|
||||
BrutalDisableLossCompensation bool `protobuf:"varint,5,opt,name=brutal_disable_loss_compensation,json=brutalDisableLossCompensation,proto3" json:"brutal_disable_loss_compensation,omitempty"`
|
||||
UdpHop *UdpHop `protobuf:"bytes,6,opt,name=udp_hop,json=udpHop,proto3" json:"udp_hop,omitempty"`
|
||||
InitStreamReceiveWindow uint64 `protobuf:"varint,7,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"`
|
||||
MaxStreamReceiveWindow uint64 `protobuf:"varint,8,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"`
|
||||
InitConnReceiveWindow uint64 `protobuf:"varint,9,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"`
|
||||
MaxConnReceiveWindow uint64 `protobuf:"varint,10,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"`
|
||||
MaxIdleTimeout int64 `protobuf:"varint,11,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"`
|
||||
KeepAlivePeriod int64 `protobuf:"varint,12,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"`
|
||||
DisablePathMtuDiscovery bool `protobuf:"varint,13,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"`
|
||||
DisableChromeParrot bool `protobuf:"varint,14,opt,name=disable_chrome_parrot,json=disableChromeParrot,proto3" json:"disable_chrome_parrot,omitempty"`
|
||||
DisableGSO bool `protobuf:"varint,15,opt,name=disableGSO,proto3" json:"disableGSO,omitempty"`
|
||||
MaxIncomingStreams int64 `protobuf:"varint,16,opt,name=max_incoming_streams,json=maxIncomingStreams,proto3" json:"max_incoming_streams,omitempty"`
|
||||
DisableStatelessReset bool `protobuf:"varint,17,opt,name=disable_stateless_reset,json=disableStatelessReset,proto3" json:"disable_stateless_reset,omitempty"`
|
||||
InitStreamReceiveWindow uint64 `protobuf:"varint,6,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"`
|
||||
MaxStreamReceiveWindow uint64 `protobuf:"varint,7,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"`
|
||||
InitConnReceiveWindow uint64 `protobuf:"varint,8,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"`
|
||||
MaxConnReceiveWindow uint64 `protobuf:"varint,9,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"`
|
||||
MaxIdleTimeout int64 `protobuf:"varint,10,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"`
|
||||
KeepAlivePeriod int64 `protobuf:"varint,11,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"`
|
||||
DisablePathMtuDiscovery bool `protobuf:"varint,12,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"`
|
||||
DisableChromeParrot bool `protobuf:"varint,13,opt,name=disable_chrome_parrot,json=disableChromeParrot,proto3" json:"disable_chrome_parrot,omitempty"`
|
||||
DisableGSO bool `protobuf:"varint,14,opt,name=disableGSO,proto3" json:"disableGSO,omitempty"`
|
||||
MaxIncomingStreams int64 `protobuf:"varint,15,opt,name=max_incoming_streams,json=maxIncomingStreams,proto3" json:"max_incoming_streams,omitempty"`
|
||||
DisableStatelessReset bool `protobuf:"varint,16,opt,name=disable_stateless_reset,json=disableStatelessReset,proto3" json:"disable_stateless_reset,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *QuicParams) Reset() {
|
||||
*x = QuicParams{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -479,7 +418,7 @@ func (x *QuicParams) String() string {
|
||||
func (*QuicParams) ProtoMessage() {}
|
||||
|
||||
func (x *QuicParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -492,7 +431,7 @@ func (x *QuicParams) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use QuicParams.ProtoReflect.Descriptor instead.
|
||||
func (*QuicParams) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{3}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *QuicParams) GetCongestion() string {
|
||||
@@ -530,13 +469,6 @@ func (x *QuicParams) GetBrutalDisableLossCompensation() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *QuicParams) GetUdpHop() *UdpHop {
|
||||
if x != nil {
|
||||
return x.UdpHop
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *QuicParams) GetInitStreamReceiveWindow() uint64 {
|
||||
if x != nil {
|
||||
return x.InitStreamReceiveWindow
|
||||
@@ -628,7 +560,7 @@ type CustomSockopt struct {
|
||||
|
||||
func (x *CustomSockopt) Reset() {
|
||||
*x = CustomSockopt{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -640,7 +572,7 @@ func (x *CustomSockopt) String() string {
|
||||
func (*CustomSockopt) ProtoMessage() {}
|
||||
|
||||
func (x *CustomSockopt) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -653,7 +585,7 @@ func (x *CustomSockopt) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CustomSockopt.ProtoReflect.Descriptor instead.
|
||||
func (*CustomSockopt) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{4}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *CustomSockopt) GetSystem() string {
|
||||
@@ -733,7 +665,7 @@ type SocketConfig struct {
|
||||
|
||||
func (x *SocketConfig) Reset() {
|
||||
*x = SocketConfig{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -745,7 +677,7 @@ func (x *SocketConfig) String() string {
|
||||
func (*SocketConfig) ProtoMessage() {}
|
||||
|
||||
func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -758,7 +690,7 @@ func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SocketConfig.ProtoReflect.Descriptor instead.
|
||||
func (*SocketConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{5}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *SocketConfig) GetMark() int32 {
|
||||
@@ -920,7 +852,7 @@ type HappyEyeballsConfig struct {
|
||||
|
||||
func (x *HappyEyeballsConfig) Reset() {
|
||||
*x = HappyEyeballsConfig{}
|
||||
mi := &file_transport_internet_config_proto_msgTypes[6]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -932,7 +864,7 @@ func (x *HappyEyeballsConfig) String() string {
|
||||
func (*HappyEyeballsConfig) ProtoMessage() {}
|
||||
|
||||
func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_config_proto_msgTypes[6]
|
||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -945,7 +877,7 @@ func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use HappyEyeballsConfig.ProtoReflect.Descriptor instead.
|
||||
func (*HappyEyeballsConfig) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{6}
|
||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *HappyEyeballsConfig) GetPrioritizeIpv6() bool {
|
||||
@@ -996,11 +928,7 @@ const file_transport_internet_config_proto_rawDesc = "" +
|
||||
"\btcpmasks\x18\v \x03(\v2 .xray.common.serial.TypedMessageR\btcpmasks\x12D\n" +
|
||||
"\vquic_params\x18\f \x01(\v2#.xray.transport.internet.QuicParamsR\n" +
|
||||
"quicParams\x12N\n" +
|
||||
"\x0fsocket_settings\x18\x06 \x01(\v2%.xray.transport.internet.SocketConfigR\x0esocketSettings\"d\n" +
|
||||
"\x06UdpHop\x12\x14\n" +
|
||||
"\x05ports\x18\x01 \x03(\rR\x05ports\x12!\n" +
|
||||
"\finterval_min\x18\x02 \x01(\x03R\vintervalMin\x12!\n" +
|
||||
"\finterval_max\x18\x03 \x01(\x03R\vintervalMax\"\xc7\x06\n" +
|
||||
"\x0fsocket_settings\x18\x06 \x01(\v2%.xray.transport.internet.SocketConfigR\x0esocketSettings\"\x8d\x06\n" +
|
||||
"\n" +
|
||||
"QuicParams\x12\x1e\n" +
|
||||
"\n" +
|
||||
@@ -1011,22 +939,21 @@ const file_transport_internet_config_proto_rawDesc = "" +
|
||||
"\tbrutal_up\x18\x03 \x01(\x04R\bbrutalUp\x12\x1f\n" +
|
||||
"\vbrutal_down\x18\x04 \x01(\x04R\n" +
|
||||
"brutalDown\x12G\n" +
|
||||
" brutal_disable_loss_compensation\x18\x05 \x01(\bR\x1dbrutalDisableLossCompensation\x128\n" +
|
||||
"\audp_hop\x18\x06 \x01(\v2\x1f.xray.transport.internet.UdpHopR\x06udpHop\x12;\n" +
|
||||
"\x1ainit_stream_receive_window\x18\a \x01(\x04R\x17initStreamReceiveWindow\x129\n" +
|
||||
"\x19max_stream_receive_window\x18\b \x01(\x04R\x16maxStreamReceiveWindow\x127\n" +
|
||||
"\x18init_conn_receive_window\x18\t \x01(\x04R\x15initConnReceiveWindow\x125\n" +
|
||||
"\x17max_conn_receive_window\x18\n" +
|
||||
" \x01(\x04R\x14maxConnReceiveWindow\x12(\n" +
|
||||
"\x10max_idle_timeout\x18\v \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
|
||||
"\x11keep_alive_period\x18\f \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
|
||||
"\x1adisable_path_mtu_discovery\x18\r \x01(\bR\x17disablePathMtuDiscovery\x122\n" +
|
||||
"\x15disable_chrome_parrot\x18\x0e \x01(\bR\x13disableChromeParrot\x12\x1e\n" +
|
||||
" brutal_disable_loss_compensation\x18\x05 \x01(\bR\x1dbrutalDisableLossCompensation\x12;\n" +
|
||||
"\x1ainit_stream_receive_window\x18\x06 \x01(\x04R\x17initStreamReceiveWindow\x129\n" +
|
||||
"\x19max_stream_receive_window\x18\a \x01(\x04R\x16maxStreamReceiveWindow\x127\n" +
|
||||
"\x18init_conn_receive_window\x18\b \x01(\x04R\x15initConnReceiveWindow\x125\n" +
|
||||
"\x17max_conn_receive_window\x18\t \x01(\x04R\x14maxConnReceiveWindow\x12(\n" +
|
||||
"\x10max_idle_timeout\x18\n" +
|
||||
" \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
|
||||
"\x11keep_alive_period\x18\v \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
|
||||
"\x1adisable_path_mtu_discovery\x18\f \x01(\bR\x17disablePathMtuDiscovery\x122\n" +
|
||||
"\x15disable_chrome_parrot\x18\r \x01(\bR\x13disableChromeParrot\x12\x1e\n" +
|
||||
"\n" +
|
||||
"disableGSO\x18\x0f \x01(\bR\n" +
|
||||
"disableGSO\x18\x0e \x01(\bR\n" +
|
||||
"disableGSO\x120\n" +
|
||||
"\x14max_incoming_streams\x18\x10 \x01(\x03R\x12maxIncomingStreams\x126\n" +
|
||||
"\x17disable_stateless_reset\x18\x11 \x01(\bR\x15disableStatelessReset\"\x93\x01\n" +
|
||||
"\x14max_incoming_streams\x18\x0f \x01(\x03R\x12maxIncomingStreams\x126\n" +
|
||||
"\x17disable_stateless_reset\x18\x10 \x01(\bR\x15disableStatelessReset\"\x93\x01\n" +
|
||||
"\rCustomSockopt\x12\x16\n" +
|
||||
"\x06system\x18\x01 \x01(\tR\x06system\x12\x18\n" +
|
||||
"\anetwork\x18\x02 \x01(\tR\anetwork\x12\x14\n" +
|
||||
@@ -1110,41 +1037,39 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_transport_internet_config_proto_goTypes = []any{
|
||||
(DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy
|
||||
(AddressPortStrategy)(0), // 1: xray.transport.internet.AddressPortStrategy
|
||||
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
|
||||
(*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig
|
||||
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
|
||||
(*UdpHop)(nil), // 5: xray.transport.internet.UdpHop
|
||||
(*QuicParams)(nil), // 6: xray.transport.internet.QuicParams
|
||||
(*CustomSockopt)(nil), // 7: xray.transport.internet.CustomSockopt
|
||||
(*SocketConfig)(nil), // 8: xray.transport.internet.SocketConfig
|
||||
(*HappyEyeballsConfig)(nil), // 9: xray.transport.internet.HappyEyeballsConfig
|
||||
(*serial.TypedMessage)(nil), // 10: xray.common.serial.TypedMessage
|
||||
(*net.IPOrDomain)(nil), // 11: xray.common.net.IPOrDomain
|
||||
(*QuicParams)(nil), // 5: xray.transport.internet.QuicParams
|
||||
(*CustomSockopt)(nil), // 6: xray.transport.internet.CustomSockopt
|
||||
(*SocketConfig)(nil), // 7: xray.transport.internet.SocketConfig
|
||||
(*HappyEyeballsConfig)(nil), // 8: xray.transport.internet.HappyEyeballsConfig
|
||||
(*serial.TypedMessage)(nil), // 9: xray.common.serial.TypedMessage
|
||||
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
|
||||
}
|
||||
var file_transport_internet_config_proto_depIdxs = []int32{
|
||||
10, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||
11, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||
9, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||
10, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||
3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
||||
10, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||
10, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
10, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
6, // 6: xray.transport.internet.StreamConfig.quic_params:type_name -> xray.transport.internet.QuicParams
|
||||
8, // 7: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||
5, // 8: xray.transport.internet.QuicParams.udp_hop:type_name -> xray.transport.internet.UdpHop
|
||||
2, // 9: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||
0, // 10: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||
7, // 11: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||
1, // 12: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||
9, // 13: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig
|
||||
14, // [14:14] is the sub-list for method output_type
|
||||
14, // [14:14] is the sub-list for method input_type
|
||||
14, // [14:14] is the sub-list for extension type_name
|
||||
14, // [14:14] is the sub-list for extension extendee
|
||||
0, // [0:14] is the sub-list for field type_name
|
||||
9, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||
9, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
9, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage
|
||||
5, // 6: xray.transport.internet.StreamConfig.quic_params:type_name -> xray.transport.internet.QuicParams
|
||||
7, // 7: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||
2, // 8: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||
0, // 9: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||
6, // 10: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||
1, // 11: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||
8, // 12: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig
|
||||
13, // [13:13] is the sub-list for method output_type
|
||||
13, // [13:13] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_config_proto_init() }
|
||||
@@ -1158,7 +1083,7 @@ func file_transport_internet_config_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_config_proto_rawDesc), len(file_transport_internet_config_proto_rawDesc)),
|
||||
NumEnums: 3,
|
||||
NumMessages: 7,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -64,30 +64,23 @@ message StreamConfig {
|
||||
SocketConfig socket_settings = 6;
|
||||
}
|
||||
|
||||
message UdpHop {
|
||||
repeated uint32 ports = 1;
|
||||
int64 interval_min = 2;
|
||||
int64 interval_max = 3;
|
||||
}
|
||||
|
||||
message QuicParams {
|
||||
string congestion = 1;
|
||||
string bbr_profile = 2;
|
||||
uint64 brutal_up = 3;
|
||||
uint64 brutal_down = 4;
|
||||
bool brutal_disable_loss_compensation = 5;
|
||||
UdpHop udp_hop = 6;
|
||||
uint64 init_stream_receive_window = 7;
|
||||
uint64 max_stream_receive_window = 8;
|
||||
uint64 init_conn_receive_window = 9;
|
||||
uint64 max_conn_receive_window = 10;
|
||||
int64 max_idle_timeout = 11;
|
||||
int64 keep_alive_period = 12;
|
||||
bool disable_path_mtu_discovery = 13;
|
||||
bool disable_chrome_parrot = 14;
|
||||
bool disableGSO = 15;
|
||||
int64 max_incoming_streams = 16;
|
||||
bool disable_stateless_reset = 17;
|
||||
uint64 init_stream_receive_window = 6;
|
||||
uint64 max_stream_receive_window = 7;
|
||||
uint64 init_conn_receive_window = 8;
|
||||
uint64 max_conn_receive_window = 9;
|
||||
int64 max_idle_timeout = 10;
|
||||
int64 keep_alive_period = 11;
|
||||
bool disable_path_mtu_discovery = 12;
|
||||
bool disable_chrome_parrot = 13;
|
||||
bool disableGSO = 14;
|
||||
int64 max_incoming_streams = 15;
|
||||
bool disable_stateless_reset = 16;
|
||||
}
|
||||
|
||||
message CustomSockopt {
|
||||
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
)
|
||||
|
||||
type Udpmask interface {
|
||||
UDP()
|
||||
|
||||
WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error)
|
||||
WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error)
|
||||
}
|
||||
@@ -21,15 +19,14 @@ type UdpmaskManager struct {
|
||||
}
|
||||
|
||||
func NewUdpmaskManager(udpmasks []Udpmask) *UdpmaskManager {
|
||||
return &UdpmaskManager{
|
||||
udpmasks: udpmasks,
|
||||
}
|
||||
slices.Reverse(udpmasks)
|
||||
return &UdpmaskManager{udpmasks: udpmasks}
|
||||
}
|
||||
|
||||
func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) {
|
||||
var sizes []int
|
||||
var conns []net.PacketConn
|
||||
for i, mask := range slices.Backward(m.udpmasks) {
|
||||
for i, mask := range m.udpmasks {
|
||||
if _, ok := mask.(headerConn); ok {
|
||||
conn, err := mask.WrapPacketConnClient(nil, i, len(m.udpmasks)-1)
|
||||
if err != nil {
|
||||
@@ -62,7 +59,7 @@ func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketCon
|
||||
func (m *UdpmaskManager) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) {
|
||||
var sizes []int
|
||||
var conns []net.PacketConn
|
||||
for i, mask := range slices.Backward(m.udpmasks) {
|
||||
for i, mask := range m.udpmasks {
|
||||
if _, ok := mask.(headerConn); ok {
|
||||
conn, err := mask.WrapPacketConnServer(nil, i, len(m.udpmasks)-1)
|
||||
if err != nil {
|
||||
@@ -195,8 +192,6 @@ func (c *headerManagerConn) WriteTo(p []byte, addr net.Addr) (n int, err error)
|
||||
}
|
||||
|
||||
type Tcpmask interface {
|
||||
TCP()
|
||||
|
||||
WrapConnClient(net.Conn) (net.Conn, error)
|
||||
WrapConnServer(net.Conn) (net.Conn, error)
|
||||
}
|
||||
@@ -206,14 +201,13 @@ type TcpmaskManager struct {
|
||||
}
|
||||
|
||||
func NewTcpmaskManager(tcpmasks []Tcpmask) *TcpmaskManager {
|
||||
return &TcpmaskManager{
|
||||
tcpmasks: tcpmasks,
|
||||
}
|
||||
slices.Reverse(tcpmasks)
|
||||
return &TcpmaskManager{tcpmasks: tcpmasks}
|
||||
}
|
||||
|
||||
func (m *TcpmaskManager) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
var err error
|
||||
for _, mask := range slices.Backward(m.tcpmasks) {
|
||||
for _, mask := range m.tcpmasks {
|
||||
raw, err = mask.WrapConnClient(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -224,7 +218,7 @@ func (m *TcpmaskManager) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
|
||||
func (m *TcpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
var err error
|
||||
for _, mask := range slices.Backward(m.tcpmasks) {
|
||||
for _, mask := range m.tcpmasks {
|
||||
raw, err = mask.WrapConnServer(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -2,9 +2,6 @@ package fragment
|
||||
|
||||
import "net"
|
||||
|
||||
func (c *Config) TCP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnClient(c, raw, false)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *TCPConfig) TCP() {}
|
||||
|
||||
func (c *TCPConfig) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnClientTCP(c, raw)
|
||||
}
|
||||
@@ -14,8 +12,6 @@ func (c *TCPConfig) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
||||
return NewConnServerTCP(c, raw)
|
||||
}
|
||||
|
||||
func (c *UDPConfig) UDP() {}
|
||||
|
||||
func (c *UDPConfig) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClientUDP(c, raw)
|
||||
}
|
||||
@@ -24,8 +20,6 @@ func (c *UDPConfig) WrapPacketConnServer(raw net.PacketConn, level int, levelCou
|
||||
return NewConnServerUDP(c, raw)
|
||||
}
|
||||
|
||||
func (c *UDPStandaloneConfig) UDP() {}
|
||||
|
||||
func (c *UDPStandaloneConfig) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClientUDPStandalone(c, raw)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *Config) UDP() {}
|
||||
|
||||
func (c *Config) HeaderConn() {}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *Config) UDP() {}
|
||||
|
||||
func (c *Config) HeaderConn() {}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *Config) UDP() {}
|
||||
|
||||
func (c *Config) HeaderConn() {}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
|
||||
@@ -2,9 +2,6 @@ package noise
|
||||
|
||||
import "net"
|
||||
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewConnClient(c, raw)
|
||||
}
|
||||
|
||||
@@ -5,15 +5,11 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/udphop"
|
||||
)
|
||||
|
||||
func (c *Config) UDP() {}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
_, ok1 := raw.(*internet.FakePacketConn)
|
||||
_, ok2 := raw.(*udphop.UdpHopPacketConn)
|
||||
if level != 0 || ok1 || ok2 {
|
||||
if level != 0 || ok1 {
|
||||
return nil, errors.New("realm requires being at the outermost level")
|
||||
}
|
||||
return NewConnClient(c, raw)
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *Config) UDP() {}
|
||||
|
||||
func (c *Config) HeaderConn() {}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
@@ -16,8 +14,6 @@ func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount
|
||||
return NewSalamanderConnServer(c, raw)
|
||||
}
|
||||
|
||||
func (c *GeckoConfig) UDP() {}
|
||||
|
||||
func (c *GeckoConfig) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return NewGeckoConnClient(c, raw)
|
||||
}
|
||||
|
||||
@@ -6,12 +6,6 @@ import (
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
func (c *Config) TCP() {
|
||||
}
|
||||
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
// Sudoku in finalmask mode is a pure appearance transform with no standalone handshake.
|
||||
// TCP always keeps classic sudoku on uplink and uses packed downlink optimization on server writes.
|
||||
func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package udphop
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
_, ok1 := raw.(*internet.FakePacketConn)
|
||||
if level != 0 || ok1 {
|
||||
return nil, errors.New("udphop requires being at the outermost level")
|
||||
}
|
||||
return NewUDPHopConn(c, raw)
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
return nil, errors.New("udphop: client only")
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/finalmask/udphop/config.proto
|
||||
|
||||
package udphop
|
||||
|
||||
import (
|
||||
internet "github.com/xtls/xray-core/transport/internet"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Sockopt *internet.SocketConfig `protobuf:"bytes,1,opt,name=sockopt,proto3" json:"sockopt,omitempty"`
|
||||
Local bool `protobuf:"varint,2,opt,name=local,proto3" json:"local,omitempty"`
|
||||
Remote bool `protobuf:"varint,3,opt,name=remote,proto3" json:"remote,omitempty"`
|
||||
RemoteOnce bool `protobuf:"varint,4,opt,name=remote_once,json=remoteOnce,proto3" json:"remote_once,omitempty"`
|
||||
IntervalMin int64 `protobuf:"varint,5,opt,name=interval_min,json=intervalMin,proto3" json:"interval_min,omitempty"`
|
||||
IntervalMax int64 `protobuf:"varint,6,opt,name=interval_max,json=intervalMax,proto3" json:"interval_max,omitempty"`
|
||||
RemotePorts []uint32 `protobuf:"varint,7,rep,packed,name=remote_ports,json=remotePorts,proto3" json:"remote_ports,omitempty"`
|
||||
RemoteIPs []string `protobuf:"bytes,8,rep,name=remoteIPs,proto3" json:"remoteIPs,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_finalmask_udphop_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_transport_internet_finalmask_udphop_config_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_finalmask_udphop_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetSockopt() *internet.SocketConfig {
|
||||
if x != nil {
|
||||
return x.Sockopt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetLocal() bool {
|
||||
if x != nil {
|
||||
return x.Local
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetRemote() bool {
|
||||
if x != nil {
|
||||
return x.Remote
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetRemoteOnce() bool {
|
||||
if x != nil {
|
||||
return x.RemoteOnce
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetIntervalMin() int64 {
|
||||
if x != nil {
|
||||
return x.IntervalMin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetIntervalMax() int64 {
|
||||
if x != nil {
|
||||
return x.IntervalMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetRemotePorts() []uint32 {
|
||||
if x != nil {
|
||||
return x.RemotePorts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetRemoteIPs() []string {
|
||||
if x != nil {
|
||||
return x.RemoteIPs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_finalmask_udphop_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_finalmask_udphop_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"0transport/internet/finalmask/udphop/config.proto\x12(xray.transport.internet.finalmask.udphop\x1a\x1ftransport/internet/config.proto\"\x9f\x02\n" +
|
||||
"\x06Config\x12?\n" +
|
||||
"\asockopt\x18\x01 \x01(\v2%.xray.transport.internet.SocketConfigR\asockopt\x12\x14\n" +
|
||||
"\x05local\x18\x02 \x01(\bR\x05local\x12\x16\n" +
|
||||
"\x06remote\x18\x03 \x01(\bR\x06remote\x12\x1f\n" +
|
||||
"\vremote_once\x18\x04 \x01(\bR\n" +
|
||||
"remoteOnce\x12!\n" +
|
||||
"\finterval_min\x18\x05 \x01(\x03R\vintervalMin\x12!\n" +
|
||||
"\finterval_max\x18\x06 \x01(\x03R\vintervalMax\x12!\n" +
|
||||
"\fremote_ports\x18\a \x03(\rR\vremotePorts\x12\x1c\n" +
|
||||
"\tremoteIPs\x18\b \x03(\tR\tremoteIPsB\x9a\x01\n" +
|
||||
",com.xray.transport.internet.finalmask.udphopP\x01Z=github.com/xtls/xray-core/transport/internet/finalmask/udphop\xaa\x02(Xray.Transport.Internet.Finalmask.Udphopb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_finalmask_udphop_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_finalmask_udphop_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_finalmask_udphop_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_finalmask_udphop_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_finalmask_udphop_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_udphop_config_proto_rawDesc), len(file_transport_internet_finalmask_udphop_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_finalmask_udphop_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_finalmask_udphop_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_transport_internet_finalmask_udphop_config_proto_goTypes = []any{
|
||||
(*Config)(nil), // 0: xray.transport.internet.finalmask.udphop.Config
|
||||
(*internet.SocketConfig)(nil), // 1: xray.transport.internet.SocketConfig
|
||||
}
|
||||
var file_transport_internet_finalmask_udphop_config_proto_depIdxs = []int32{
|
||||
1, // 0: xray.transport.internet.finalmask.udphop.Config.sockopt:type_name -> xray.transport.internet.SocketConfig
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_finalmask_udphop_config_proto_init() }
|
||||
func file_transport_internet_finalmask_udphop_config_proto_init() {
|
||||
if File_transport_internet_finalmask_udphop_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_udphop_config_proto_rawDesc), len(file_transport_internet_finalmask_udphop_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_finalmask_udphop_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_finalmask_udphop_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_finalmask_udphop_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_finalmask_udphop_config_proto = out.File
|
||||
file_transport_internet_finalmask_udphop_config_proto_goTypes = nil
|
||||
file_transport_internet_finalmask_udphop_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.finalmask.udphop;
|
||||
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Udphop";
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/udphop";
|
||||
option java_package = "com.xray.transport.internet.finalmask.udphop";
|
||||
option java_multiple_files = true;
|
||||
|
||||
import "transport/internet/config.proto";
|
||||
|
||||
message Config {
|
||||
xray.transport.internet.SocketConfig sockopt = 1;
|
||||
bool local = 2;
|
||||
bool remote = 3;
|
||||
bool remote_once = 4;
|
||||
int64 interval_min = 5;
|
||||
int64 interval_max = 6;
|
||||
repeated uint32 remote_ports = 7;
|
||||
repeated string remoteIPs = 8;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
package udphop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
goerrors "errors"
|
||||
"io"
|
||||
mrand "math/rand"
|
||||
gonet "net"
|
||||
"net/netip"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
var pool = sync.Pool{
|
||||
New: func() any {
|
||||
return make([]byte, finalmask.UDPSize)
|
||||
},
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
p []byte
|
||||
addr net.Addr
|
||||
err error
|
||||
}
|
||||
|
||||
type udpHopConn struct {
|
||||
conn net.PacketConn
|
||||
sockopt *internet.SocketConfig
|
||||
local bool
|
||||
remote bool
|
||||
remoteOnce bool
|
||||
|
||||
intervalMin int64
|
||||
intervalMax int64
|
||||
remotePorts []uint32
|
||||
remoteIPs []netip.Prefix
|
||||
|
||||
deadline time.Time
|
||||
readDeadline time.Time
|
||||
writeDeadline time.Time
|
||||
|
||||
pre net.PacketConn
|
||||
cur net.PacketConn
|
||||
addr *net.UDPAddr
|
||||
readCh chan packet
|
||||
closeCh chan struct{}
|
||||
wg sync.WaitGroup
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewUDPHopConn(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
if c.IntervalMin < 5 || c.IntervalMax < 5 {
|
||||
return nil, errors.New("invalid interval")
|
||||
}
|
||||
remoteIPs := make([]netip.Prefix, 0, len(c.RemoteIPs))
|
||||
for _, ip := range c.RemoteIPs {
|
||||
remoteIPs = append(remoteIPs, netip.MustParsePrefix(ip))
|
||||
}
|
||||
conn := &udpHopConn{
|
||||
conn: raw,
|
||||
sockopt: c.Sockopt,
|
||||
local: c.Local,
|
||||
remote: c.Remote,
|
||||
remoteOnce: c.RemoteOnce,
|
||||
|
||||
intervalMin: c.IntervalMin,
|
||||
intervalMax: c.IntervalMax,
|
||||
remotePorts: c.RemotePorts,
|
||||
remoteIPs: remoteIPs,
|
||||
|
||||
readCh: make(chan packet),
|
||||
closeCh: make(chan struct{}),
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *udpHopConn) closed() bool {
|
||||
select {
|
||||
case <-c.closeCh:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpHopConn) hop(addr *net.UDPAddr) {
|
||||
if c.closed() {
|
||||
return
|
||||
}
|
||||
newAddr := &net.UDPAddr{IP: addr.IP, Port: addr.Port}
|
||||
newConn := c.conn
|
||||
if c.remote || c.remoteOnce && c.addr == nil {
|
||||
if len(c.remotePorts) > 0 {
|
||||
newAddr.Port = int(c.remotePorts[mrand.Intn(len(c.remotePorts))])
|
||||
}
|
||||
if len(c.remoteIPs) > 0 {
|
||||
newAddr.IP = randPrefix(c.remoteIPs[mrand.Intn(len(c.remoteIPs))])
|
||||
}
|
||||
}
|
||||
if c.local {
|
||||
raw, err := internet.DialSystem(context.Background(), net.UDPDestination(net.IPAddress(newAddr.IP), net.Port(newAddr.Port)), c.sockopt)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(context.Background(), err, "hop err")
|
||||
return
|
||||
}
|
||||
switch c := raw.(type) {
|
||||
case *internet.PacketConnWrapper:
|
||||
newConn = c.PacketConn
|
||||
case *cnc.Connection:
|
||||
newConn = &internet.FakePacketConn{Conn: c}
|
||||
default:
|
||||
panic(reflect.TypeOf(c))
|
||||
}
|
||||
newConn.SetDeadline(c.deadline)
|
||||
newConn.SetReadDeadline(c.readDeadline)
|
||||
newConn.SetWriteDeadline(c.writeDeadline)
|
||||
if c.pre != nil {
|
||||
_ = c.pre.Close()
|
||||
}
|
||||
c.pre = c.cur
|
||||
c.wg.Add(1)
|
||||
go c.recv(newConn)
|
||||
}
|
||||
c.addr = newAddr
|
||||
c.cur = newConn
|
||||
}
|
||||
|
||||
func (c *udpHopConn) recv(conn net.PacketConn) {
|
||||
defer c.wg.Done()
|
||||
|
||||
for {
|
||||
if c.closed() {
|
||||
return
|
||||
}
|
||||
p := pool.Get().([]byte)
|
||||
n, addr, err := conn.ReadFrom(p)
|
||||
if err != nil {
|
||||
pool.Put(p[:cap(p)])
|
||||
if goerrors.Is(err, io.EOF) || goerrors.Is(err, io.ErrClosedPipe) || goerrors.Is(err, gonet.ErrClosed) {
|
||||
break
|
||||
}
|
||||
var netErr net.Error
|
||||
if goerrors.As(err, &netErr) && netErr.Timeout() {
|
||||
select {
|
||||
case c.readCh <- packet{err: err}:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
errors.LogErrorInner(context.Background(), err, "recv err")
|
||||
continue
|
||||
}
|
||||
select {
|
||||
case c.readCh <- packet{p: p[:n], addr: addr}:
|
||||
case <-c.closeCh:
|
||||
pool.Put(p[:cap(p)])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpHopConn) hopLoop() {
|
||||
ticker := time.NewTicker(time.Second * time.Duration(crypto.RandBetween(c.intervalMin, c.intervalMax+1)))
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
ticker.Reset(time.Second * time.Duration(crypto.RandBetween(c.intervalMin, c.intervalMax+1)))
|
||||
c.mu.Lock()
|
||||
c.hop(c.addr)
|
||||
c.mu.Unlock()
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpHopConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
packet, ok := <-c.readCh
|
||||
if ok {
|
||||
if packet.p != nil {
|
||||
n = copy(p, packet.p)
|
||||
pool.Put(packet.p[:cap(packet.p)])
|
||||
}
|
||||
return n, packet.addr, packet.err
|
||||
}
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
|
||||
func (c *udpHopConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.cur == nil {
|
||||
c.hop(addr.(*net.UDPAddr))
|
||||
if c.cur == nil {
|
||||
return 0, nil
|
||||
}
|
||||
go c.hopLoop()
|
||||
}
|
||||
|
||||
_, err = c.cur.WriteTo(p, c.addr)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(context.Background(), err, "send err")
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *udpHopConn) Close() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.closed() {
|
||||
return nil
|
||||
}
|
||||
close(c.closeCh)
|
||||
if c.pre != nil {
|
||||
_ = c.pre.Close()
|
||||
}
|
||||
if c.cur != nil {
|
||||
_ = c.cur.Close()
|
||||
}
|
||||
_ = c.conn.Close()
|
||||
c.wg.Wait()
|
||||
select {
|
||||
case p := <-c.readCh:
|
||||
if p.p != nil {
|
||||
pool.Put(p.p[:cap(p.p)])
|
||||
}
|
||||
default:
|
||||
}
|
||||
close(c.readCh)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpHopConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *udpHopConn) SetDeadline(t time.Time) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.deadline = t
|
||||
if c.pre != nil {
|
||||
_ = c.pre.SetDeadline(t)
|
||||
}
|
||||
if c.cur != nil {
|
||||
_ = c.cur.SetDeadline(t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpHopConn) SetReadDeadline(t time.Time) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.readDeadline = t
|
||||
if c.pre != nil {
|
||||
_ = c.pre.SetReadDeadline(t)
|
||||
}
|
||||
if c.cur != nil {
|
||||
_ = c.cur.SetReadDeadline(t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpHopConn) SetWriteDeadline(t time.Time) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.writeDeadline = t
|
||||
if c.pre != nil {
|
||||
_ = c.pre.SetWriteDeadline(t)
|
||||
}
|
||||
if c.cur != nil {
|
||||
_ = c.cur.SetWriteDeadline(t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func randPrefix(p netip.Prefix) []byte {
|
||||
if p.IsSingleIP() {
|
||||
return p.Addr().AsSlice()
|
||||
}
|
||||
b := p.Addr().AsSlice()
|
||||
prefix := p.Bits()
|
||||
var new [16]byte
|
||||
common.Must2(rand.Read(new[:len(b)]))
|
||||
i := prefix / 8
|
||||
j := prefix % 8
|
||||
if i+1 < len(b) {
|
||||
copy(b[i+1:], new[i+1:])
|
||||
}
|
||||
mask := byte(0xff << (8 - j))
|
||||
b[i] = (b[i] & mask) | (new[i] &^ mask)
|
||||
return b
|
||||
}
|
||||
@@ -4,9 +4,6 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
// _, ok1 := raw.(*internet.FakePacketConn)
|
||||
// _, ok2 := raw.(*udphop.UdpHopPacketConn)
|
||||
|
||||
@@ -45,7 +45,8 @@ type xicmpConnClient struct {
|
||||
id int
|
||||
seq int
|
||||
readCh chan packet
|
||||
closedCh chan struct{}
|
||||
closeCh chan struct{}
|
||||
wg sync.WaitGroup
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -81,9 +82,10 @@ func NewConnClient(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
id: mathrand.Intn(65536),
|
||||
seq: 1,
|
||||
readCh: make(chan packet),
|
||||
closedCh: make(chan struct{}),
|
||||
closeCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
conn.wg.Add(2)
|
||||
go conn.recv4()
|
||||
go conn.recv6()
|
||||
|
||||
@@ -96,7 +98,7 @@ func (c *xicmpConnClient) ring(a, b uint16) uint16 {
|
||||
|
||||
func (c *xicmpConnClient) closed() bool {
|
||||
select {
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -104,8 +106,9 @@ func (c *xicmpConnClient) closed() bool {
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) recv4() {
|
||||
var b [finalmask.UDPSize]byte
|
||||
defer c.wg.Done()
|
||||
|
||||
var b [finalmask.UDPSize]byte
|
||||
for {
|
||||
if c.closed() {
|
||||
return
|
||||
@@ -119,10 +122,11 @@ func (c *xicmpConnClient) recv4() {
|
||||
case c.readCh <- packet{
|
||||
err: err,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
errors.LogErrorInner(context.Background(), err, "recv4 err")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -166,7 +170,7 @@ func (c *xicmpConnClient) recv4() {
|
||||
p: p,
|
||||
addr: addr,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
pool.Put(p)
|
||||
return
|
||||
}
|
||||
@@ -174,11 +178,12 @@ func (c *xicmpConnClient) recv4() {
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) recv6() {
|
||||
var b [finalmask.UDPSize]byte
|
||||
defer c.wg.Done()
|
||||
|
||||
var b [finalmask.UDPSize]byte
|
||||
for {
|
||||
if c.closed() {
|
||||
break
|
||||
return
|
||||
}
|
||||
|
||||
n, addr, err := c.icmp6.ReadFrom(b[:])
|
||||
@@ -189,10 +194,11 @@ func (c *xicmpConnClient) recv6() {
|
||||
case c.readCh <- packet{
|
||||
err: err,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
errors.LogErrorInner(context.Background(), err, "recv6 err")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -236,7 +242,7 @@ func (c *xicmpConnClient) recv6() {
|
||||
p: p,
|
||||
addr: addr,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
pool.Put(p)
|
||||
return
|
||||
}
|
||||
@@ -244,16 +250,15 @@ func (c *xicmpConnClient) recv6() {
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
select {
|
||||
case packet := <-c.readCh:
|
||||
packet, ok := <-c.readCh
|
||||
if ok {
|
||||
if packet.p != nil {
|
||||
n = copy(p, packet.p)
|
||||
pool.Put(packet.p)
|
||||
}
|
||||
return n, packet.addr, packet.err
|
||||
case <-c.closedCh:
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
|
||||
func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
@@ -294,10 +299,9 @@ func (c *xicmpConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
errors.LogErrorInner(context.Background(), err, "xicmp write")
|
||||
errors.LogErrorInner(context.Background(), err, "send err")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -307,10 +311,19 @@ func (c *xicmpConnClient) Close() error {
|
||||
if c.closed() {
|
||||
return nil
|
||||
}
|
||||
close(c.closedCh)
|
||||
close(c.closeCh)
|
||||
_ = c.icmp4.Close()
|
||||
_ = c.icmp6.Close()
|
||||
_ = c.conn.Close()
|
||||
c.wg.Wait()
|
||||
select {
|
||||
case p := <-c.readCh:
|
||||
if p.p != nil {
|
||||
pool.Put(p.p)
|
||||
}
|
||||
default:
|
||||
}
|
||||
close(c.readCh)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,16 +5,11 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/udphop"
|
||||
)
|
||||
|
||||
func (c *Config) UDP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
|
||||
_, ok1 := raw.(*internet.FakePacketConn)
|
||||
_, ok2 := raw.(*udphop.UdpHopPacketConn)
|
||||
if level != 0 || ok1 || ok2 {
|
||||
if level != 0 || ok1 {
|
||||
return nil, errors.New("xicmp requires being at the outermost level")
|
||||
}
|
||||
return NewConnClient(c, raw)
|
||||
|
||||
@@ -37,14 +37,15 @@ type record struct {
|
||||
}
|
||||
|
||||
type xicmpConnServer struct {
|
||||
conn net.PacketConn
|
||||
icmp4 *icmp.PacketConn
|
||||
icmp6 *icmp.PacketConn
|
||||
ips map[netip.Addr]struct{}
|
||||
rec map[string]record
|
||||
readCh chan packet
|
||||
closedCh chan struct{}
|
||||
mu sync.Mutex
|
||||
conn net.PacketConn
|
||||
icmp4 *icmp.PacketConn
|
||||
icmp6 *icmp.PacketConn
|
||||
ips map[netip.Addr]struct{}
|
||||
rec map[string]record
|
||||
readCh chan packet
|
||||
closeCh chan struct{}
|
||||
wg sync.WaitGroup
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
@@ -63,16 +64,17 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
}
|
||||
|
||||
conn := &xicmpConnServer{
|
||||
conn: raw,
|
||||
icmp4: icmp4,
|
||||
icmp6: icmp6,
|
||||
ips: ips,
|
||||
rec: make(map[string]record),
|
||||
readCh: make(chan packet),
|
||||
closedCh: make(chan struct{}),
|
||||
conn: raw,
|
||||
icmp4: icmp4,
|
||||
icmp6: icmp6,
|
||||
ips: ips,
|
||||
rec: make(map[string]record),
|
||||
readCh: make(chan packet),
|
||||
closeCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
go conn.clean()
|
||||
conn.wg.Add(2)
|
||||
go conn.recv4()
|
||||
go conn.recv6()
|
||||
|
||||
@@ -81,7 +83,7 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
|
||||
func (c *xicmpConnServer) closed() bool {
|
||||
select {
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -102,15 +104,16 @@ func (c *xicmpConnServer) clean() {
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) recv4() {
|
||||
var b [finalmask.UDPSize]byte
|
||||
defer c.wg.Done()
|
||||
|
||||
var b [finalmask.UDPSize]byte
|
||||
for {
|
||||
if c.closed() {
|
||||
return
|
||||
@@ -124,10 +127,11 @@ func (c *xicmpConnServer) recv4() {
|
||||
case c.readCh <- packet{
|
||||
err: err,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
errors.LogErrorInner(context.Background(), err, "recv4 err")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -179,7 +183,7 @@ func (c *xicmpConnServer) recv4() {
|
||||
p: p,
|
||||
addr: cAddr,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
pool.Put(p)
|
||||
return
|
||||
}
|
||||
@@ -187,8 +191,9 @@ func (c *xicmpConnServer) recv4() {
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) recv6() {
|
||||
var b [finalmask.UDPSize]byte
|
||||
defer c.wg.Done()
|
||||
|
||||
var b [finalmask.UDPSize]byte
|
||||
for {
|
||||
if c.closed() {
|
||||
return
|
||||
@@ -202,10 +207,11 @@ func (c *xicmpConnServer) recv6() {
|
||||
case c.readCh <- packet{
|
||||
err: err,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
errors.LogErrorInner(context.Background(), err, "recv6 err")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -257,7 +263,7 @@ func (c *xicmpConnServer) recv6() {
|
||||
p: p,
|
||||
addr: cAddr,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
pool.Put(p)
|
||||
return
|
||||
}
|
||||
@@ -265,16 +271,15 @@ func (c *xicmpConnServer) recv6() {
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
select {
|
||||
case packet := <-c.readCh:
|
||||
packet, ok := <-c.readCh
|
||||
if ok {
|
||||
if packet.p != nil {
|
||||
n = copy(p, packet.p)
|
||||
pool.Put(packet.p)
|
||||
}
|
||||
return n, packet.addr, packet.err
|
||||
case <-c.closedCh:
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
@@ -310,10 +315,9 @@ func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
errors.LogErrorInner(context.Background(), err, "xicmp write")
|
||||
errors.LogErrorInner(context.Background(), err, "send err")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -323,10 +327,19 @@ func (c *xicmpConnServer) Close() error {
|
||||
if c.closed() {
|
||||
return nil
|
||||
}
|
||||
close(c.closedCh)
|
||||
close(c.closeCh)
|
||||
_ = c.icmp4.Close()
|
||||
_ = c.icmp6.Close()
|
||||
_ = c.conn.Close()
|
||||
c.wg.Wait()
|
||||
select {
|
||||
case p := <-c.readCh:
|
||||
if p.p != nil {
|
||||
pool.Put(p.p)
|
||||
}
|
||||
default:
|
||||
}
|
||||
close(c.readCh)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -39,16 +39,17 @@ type record struct {
|
||||
}
|
||||
|
||||
type xicmpConnServer struct {
|
||||
conn net.PacketConn
|
||||
icmp4 *icmp.PacketConn
|
||||
icmp6 *icmp.PacketConn
|
||||
ipv4PC *ipv4.PacketConn
|
||||
ipv6PC *ipv6.PacketConn
|
||||
ips map[netip.Addr]struct{}
|
||||
rec map[string]record
|
||||
readCh chan packet
|
||||
closedCh chan struct{}
|
||||
mu sync.Mutex
|
||||
conn net.PacketConn
|
||||
icmp4 *icmp.PacketConn
|
||||
icmp6 *icmp.PacketConn
|
||||
ipv4PC *ipv4.PacketConn
|
||||
ipv6PC *ipv6.PacketConn
|
||||
ips map[netip.Addr]struct{}
|
||||
rec map[string]record
|
||||
readCh chan packet
|
||||
closeCh chan struct{}
|
||||
wg sync.WaitGroup
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
@@ -67,21 +68,22 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
}
|
||||
|
||||
conn := &xicmpConnServer{
|
||||
conn: raw,
|
||||
icmp4: icmp4,
|
||||
icmp6: icmp6,
|
||||
ipv4PC: icmp4.IPv4PacketConn(),
|
||||
ipv6PC: icmp6.IPv6PacketConn(),
|
||||
ips: ips,
|
||||
rec: make(map[string]record),
|
||||
readCh: make(chan packet),
|
||||
closedCh: make(chan struct{}),
|
||||
conn: raw,
|
||||
icmp4: icmp4,
|
||||
icmp6: icmp6,
|
||||
ipv4PC: icmp4.IPv4PacketConn(),
|
||||
ipv6PC: icmp6.IPv6PacketConn(),
|
||||
ips: ips,
|
||||
rec: make(map[string]record),
|
||||
readCh: make(chan packet),
|
||||
closeCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
common.Must(conn.ipv4PC.SetControlMessage(ipv4.FlagDst, true))
|
||||
common.Must(conn.ipv6PC.SetControlMessage(ipv6.FlagDst, true))
|
||||
|
||||
go conn.clean()
|
||||
conn.wg.Add(2)
|
||||
go conn.recv4()
|
||||
go conn.recv6()
|
||||
|
||||
@@ -90,7 +92,7 @@ func NewConnServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
||||
|
||||
func (c *xicmpConnServer) closed() bool {
|
||||
select {
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -111,15 +113,16 @@ func (c *xicmpConnServer) clean() {
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) recv4() {
|
||||
var b [finalmask.UDPSize]byte
|
||||
defer c.wg.Done()
|
||||
|
||||
var b [finalmask.UDPSize]byte
|
||||
for {
|
||||
if c.closed() {
|
||||
return
|
||||
@@ -133,10 +136,11 @@ func (c *xicmpConnServer) recv4() {
|
||||
case c.readCh <- packet{
|
||||
err: err,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
errors.LogErrorInner(context.Background(), err, "recv4 err")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -189,7 +193,7 @@ func (c *xicmpConnServer) recv4() {
|
||||
p: p,
|
||||
addr: cAddr,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
pool.Put(p)
|
||||
return
|
||||
}
|
||||
@@ -197,8 +201,9 @@ func (c *xicmpConnServer) recv4() {
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) recv6() {
|
||||
var b [finalmask.UDPSize]byte
|
||||
defer c.wg.Done()
|
||||
|
||||
var b [finalmask.UDPSize]byte
|
||||
for {
|
||||
if c.closed() {
|
||||
return
|
||||
@@ -212,10 +217,11 @@ func (c *xicmpConnServer) recv6() {
|
||||
case c.readCh <- packet{
|
||||
err: err,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
errors.LogErrorInner(context.Background(), err, "recv6 err")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -268,7 +274,7 @@ func (c *xicmpConnServer) recv6() {
|
||||
p: p,
|
||||
addr: cAddr,
|
||||
}:
|
||||
case <-c.closedCh:
|
||||
case <-c.closeCh:
|
||||
pool.Put(p)
|
||||
return
|
||||
}
|
||||
@@ -276,16 +282,15 @@ func (c *xicmpConnServer) recv6() {
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
select {
|
||||
case packet := <-c.readCh:
|
||||
packet, ok := <-c.readCh
|
||||
if ok {
|
||||
if packet.p != nil {
|
||||
n = copy(p, packet.p)
|
||||
pool.Put(packet.p)
|
||||
}
|
||||
return n, packet.addr, packet.err
|
||||
case <-c.closedCh:
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
|
||||
func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
@@ -321,10 +326,9 @@ func (c *xicmpConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
errors.LogErrorInner(context.Background(), err, "xicmp write")
|
||||
errors.LogErrorInner(context.Background(), err, "send err")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -334,10 +338,19 @@ func (c *xicmpConnServer) Close() error {
|
||||
if c.closed() {
|
||||
return nil
|
||||
}
|
||||
close(c.closedCh)
|
||||
close(c.closeCh)
|
||||
_ = c.icmp4.Close()
|
||||
_ = c.icmp6.Close()
|
||||
_ = c.conn.Close()
|
||||
c.wg.Wait()
|
||||
select {
|
||||
case p := <-c.readCh:
|
||||
if p.p != nil {
|
||||
pool.Put(p.p)
|
||||
}
|
||||
default:
|
||||
}
|
||||
close(c.readCh)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,6 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *Config) TCP() {
|
||||
}
|
||||
|
||||
func (c *Config) WrapConnClient(conn net.Conn) (net.Conn, error) {
|
||||
profiles, err := profilesFromConfig(c.Profiles)
|
||||
if err != nil {
|
||||
|
||||
@@ -103,14 +103,11 @@ func (c *InterConn) Update() {
|
||||
|
||||
func (c *InterConn) Read(p []byte) (int, error) {
|
||||
b, ok := <-c.ch
|
||||
if !ok {
|
||||
return 0, io.EOF
|
||||
if ok {
|
||||
c.Update()
|
||||
return copy(p, b), nil
|
||||
}
|
||||
if len(p) < len(b) {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
c.Update()
|
||||
return copy(p, b), nil
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
func (c *InterConn) Write(p []byte) (int, error) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package hysteria
|
||||
import (
|
||||
"context"
|
||||
go_tls "crypto/tls"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
@@ -22,7 +21,6 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/congestion"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/congestion/bbr"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/udphop"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
@@ -78,7 +76,6 @@ func (c *client) dial(ctx context.Context) error {
|
||||
if quicParams == nil {
|
||||
quicParams = &internet.QuicParams{
|
||||
BbrProfile: string(bbr.ProfileStandard),
|
||||
UdpHop: &internet.UdpHop{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,35 +112,8 @@ func (c *client) dial(ctx context.Context) error {
|
||||
// quicConfig.KeepAlivePeriod = 10 * time.Second
|
||||
// }
|
||||
|
||||
udpHopDialer := func(addr *net.UDPAddr) (net.PacketConn, error) {
|
||||
conn, err := internet.DialSystem(ctx, net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)), c.socketConfig)
|
||||
if err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "skip hop: failed to dial to dest")
|
||||
return nil, errors.New("")
|
||||
}
|
||||
|
||||
var pktConn net.PacketConn
|
||||
|
||||
switch c := conn.(type) {
|
||||
case *internet.PacketConnWrapper:
|
||||
pktConn = c.PacketConn
|
||||
case *cnc.Connection:
|
||||
pktConn = &internet.FakePacketConn{Conn: c}
|
||||
default:
|
||||
panic(reflect.TypeOf(c))
|
||||
}
|
||||
|
||||
return pktConn, nil
|
||||
}
|
||||
|
||||
var pktConn net.PacketConn
|
||||
var udpAddr *net.UDPAddr
|
||||
var index int
|
||||
|
||||
if len(quicParams.UdpHop.Ports) > 0 {
|
||||
index = rand.Intn(len(quicParams.UdpHop.Ports))
|
||||
c.dest.Port = net.Port(quicParams.UdpHop.Ports[index])
|
||||
}
|
||||
|
||||
raw, err := internet.DialSystem(ctx, c.dest, c.socketConfig)
|
||||
if err != nil {
|
||||
@@ -160,10 +130,6 @@ func (c *client) dial(ctx context.Context) error {
|
||||
panic(reflect.TypeOf(c))
|
||||
}
|
||||
|
||||
if len(quicParams.UdpHop.Ports) > 0 {
|
||||
pktConn = udphop.NewUDPHopPacketConn(udphop.ToAddrs(udpAddr.IP, quicParams.UdpHop.Ports), time.Duration(quicParams.UdpHop.IntervalMin)*time.Second, time.Duration(quicParams.UdpHop.IntervalMax)*time.Second, udpHopDialer, pktConn, index)
|
||||
}
|
||||
|
||||
if c.udpmaskManager != nil {
|
||||
newConn, err := c.udpmaskManager.WrapPacketConnClient(pktConn)
|
||||
if err != nil {
|
||||
|
||||
@@ -281,7 +281,6 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
|
||||
if quicParams == nil {
|
||||
quicParams = &internet.QuicParams{
|
||||
BbrProfile: string(bbr.ProfileStandard),
|
||||
UdpHop: &internet.UdpHop{},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
package udphop
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||
)
|
||||
|
||||
const (
|
||||
packetQueueSize = 1024
|
||||
udpBufferSize = finalmask.UDPSize
|
||||
|
||||
defaultHopInterval = 30 * time.Second
|
||||
)
|
||||
|
||||
type UdpHopPacketConn struct {
|
||||
Addrs []net.Addr
|
||||
HopIntervalMin time.Duration
|
||||
HopIntervalMax time.Duration
|
||||
ListenUDPFunc func(addr *net.UDPAddr) (net.PacketConn, error)
|
||||
|
||||
connMutex sync.RWMutex
|
||||
prevConn net.PacketConn
|
||||
currentConn net.PacketConn
|
||||
addrIndex int
|
||||
|
||||
deadline time.Time
|
||||
readDeadline time.Time
|
||||
writeDeadline time.Time
|
||||
|
||||
recvQueue chan *udpPacket
|
||||
closeChan chan struct{}
|
||||
closed bool
|
||||
|
||||
bufPool sync.Pool
|
||||
}
|
||||
|
||||
type udpPacket struct {
|
||||
Buf []byte
|
||||
N int
|
||||
Addr net.Addr
|
||||
Err error
|
||||
}
|
||||
|
||||
func NewUDPHopPacketConn(addrs []net.Addr, hopIntervalMin time.Duration, hopIntervalMax time.Duration, listenUDPFunc func(addr *net.UDPAddr) (net.PacketConn, error), currentConn net.PacketConn, addrIndex int) net.PacketConn {
|
||||
if len(addrs) == 0 {
|
||||
panic("len(addrs) == 0")
|
||||
}
|
||||
if hopIntervalMin == 0 {
|
||||
hopIntervalMin = defaultHopInterval
|
||||
}
|
||||
if hopIntervalMax == 0 {
|
||||
hopIntervalMax = defaultHopInterval
|
||||
}
|
||||
if hopIntervalMin < 5*time.Second {
|
||||
panic("hopIntervalMin < 5*time.Second")
|
||||
}
|
||||
if hopIntervalMax < 5*time.Second {
|
||||
panic("hopIntervalMax < 5*time.Second")
|
||||
}
|
||||
if hopIntervalMax < hopIntervalMin {
|
||||
panic("hopIntervalMax < hopIntervalMin")
|
||||
}
|
||||
if listenUDPFunc == nil {
|
||||
panic("listenUDPFunc is nil")
|
||||
}
|
||||
hConn := &UdpHopPacketConn{
|
||||
Addrs: addrs,
|
||||
HopIntervalMin: hopIntervalMin,
|
||||
HopIntervalMax: hopIntervalMax,
|
||||
ListenUDPFunc: listenUDPFunc,
|
||||
prevConn: nil,
|
||||
currentConn: currentConn,
|
||||
addrIndex: addrIndex,
|
||||
recvQueue: make(chan *udpPacket, packetQueueSize),
|
||||
closeChan: make(chan struct{}),
|
||||
bufPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, udpBufferSize)
|
||||
},
|
||||
},
|
||||
}
|
||||
go hConn.recvLoop(hConn.currentConn)
|
||||
go hConn.hopLoop()
|
||||
return hConn
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) recvLoop(conn net.PacketConn) {
|
||||
for {
|
||||
buf := u.bufPool.Get().([]byte)
|
||||
n, addr, err := conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
u.bufPool.Put(buf)
|
||||
var netErr net.Error
|
||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||
u.recvQueue <- &udpPacket{nil, 0, nil, netErr}
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
select {
|
||||
case u.recvQueue <- &udpPacket{buf, n, addr, nil}:
|
||||
default:
|
||||
u.bufPool.Put(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) hopLoop() {
|
||||
timer := time.NewTimer(u.nextHopInterval())
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
u.hop()
|
||||
timer.Reset(u.nextHopInterval())
|
||||
case <-u.closeChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) nextHopInterval() time.Duration {
|
||||
if u.HopIntervalMin == u.HopIntervalMax {
|
||||
return u.HopIntervalMin
|
||||
}
|
||||
return u.HopIntervalMin + time.Duration(rand.Int63n(int64(u.HopIntervalMax-u.HopIntervalMin)+1))
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) hop() {
|
||||
u.connMutex.Lock()
|
||||
defer u.connMutex.Unlock()
|
||||
if u.closed {
|
||||
return
|
||||
}
|
||||
addrIndex := rand.Intn(len(u.Addrs))
|
||||
newConn, err := u.ListenUDPFunc(u.Addrs[addrIndex].(*net.UDPAddr))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if u.prevConn != nil {
|
||||
_ = u.prevConn.Close()
|
||||
}
|
||||
u.prevConn = u.currentConn
|
||||
u.addrIndex = addrIndex
|
||||
u.currentConn = newConn
|
||||
if !u.deadline.IsZero() {
|
||||
_ = u.currentConn.SetDeadline(u.deadline)
|
||||
}
|
||||
if !u.readDeadline.IsZero() {
|
||||
_ = u.currentConn.SetReadDeadline(u.readDeadline)
|
||||
}
|
||||
if !u.writeDeadline.IsZero() {
|
||||
_ = u.currentConn.SetWriteDeadline(u.writeDeadline)
|
||||
}
|
||||
go u.recvLoop(newConn)
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
|
||||
for {
|
||||
select {
|
||||
case p := <-u.recvQueue:
|
||||
if p.Err != nil {
|
||||
return 0, nil, p.Err
|
||||
}
|
||||
n := copy(b, p.Buf[:p.N])
|
||||
u.bufPool.Put(p.Buf)
|
||||
return n, p.Addr, nil
|
||||
case <-u.closeChan:
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
u.connMutex.RLock()
|
||||
defer u.connMutex.RUnlock()
|
||||
if u.closed {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
return u.currentConn.WriteTo(b, u.Addrs[u.addrIndex])
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) Close() error {
|
||||
u.connMutex.Lock()
|
||||
defer u.connMutex.Unlock()
|
||||
if u.closed {
|
||||
return nil
|
||||
}
|
||||
if u.prevConn != nil {
|
||||
_ = u.prevConn.Close()
|
||||
}
|
||||
err := u.currentConn.Close()
|
||||
close(u.closeChan)
|
||||
u.closed = true
|
||||
u.Addrs = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) LocalAddr() net.Addr {
|
||||
u.connMutex.RLock()
|
||||
defer u.connMutex.RUnlock()
|
||||
return u.currentConn.LocalAddr()
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) SetDeadline(t time.Time) error {
|
||||
u.connMutex.Lock()
|
||||
defer u.connMutex.Unlock()
|
||||
u.deadline = t
|
||||
u.readDeadline = t
|
||||
u.writeDeadline = t
|
||||
if u.prevConn != nil {
|
||||
_ = u.prevConn.SetDeadline(t)
|
||||
}
|
||||
return u.currentConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) SetReadDeadline(t time.Time) error {
|
||||
u.connMutex.Lock()
|
||||
defer u.connMutex.Unlock()
|
||||
u.deadline = time.Time{}
|
||||
u.readDeadline = t
|
||||
if u.prevConn != nil {
|
||||
_ = u.prevConn.SetReadDeadline(t)
|
||||
}
|
||||
return u.currentConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (u *UdpHopPacketConn) SetWriteDeadline(t time.Time) error {
|
||||
u.connMutex.Lock()
|
||||
defer u.connMutex.Unlock()
|
||||
u.deadline = time.Time{}
|
||||
u.writeDeadline = t
|
||||
if u.prevConn != nil {
|
||||
_ = u.prevConn.SetWriteDeadline(t)
|
||||
}
|
||||
return u.currentConn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func ToAddrs(ip net.IP, ports []uint32) []net.Addr {
|
||||
var addrs []net.Addr
|
||||
for _, port := range ports {
|
||||
addr := &net.UDPAddr{
|
||||
IP: ip,
|
||||
Port: int(port),
|
||||
}
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
gotls "crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"net/url"
|
||||
@@ -28,7 +27,6 @@ import (
|
||||
"github.com/xtls/xray-core/transport/internet/browser_dialer"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/congestion"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/congestion/bbr"
|
||||
"github.com/xtls/xray-core/transport/internet/hysteria/udphop"
|
||||
"github.com/xtls/xray-core/transport/internet/reality"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
@@ -162,7 +160,6 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||
if quicParams == nil {
|
||||
quicParams = &internet.QuicParams{
|
||||
BbrProfile: string(bbr.ProfileStandard),
|
||||
UdpHop: &internet.UdpHop{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,35 +195,8 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||
QUICConfig: quicConfig,
|
||||
TLSClientConfig: gotlsConfig,
|
||||
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (*quic.Conn, error) {
|
||||
udpHopDialer := func(addr *net.UDPAddr) (net.PacketConn, error) {
|
||||
conn, err := internet.DialSystem(ctx, net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)), streamSettings.SocketSettings)
|
||||
if err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "skip hop: failed to dial to dest")
|
||||
return nil, errors.New("")
|
||||
}
|
||||
|
||||
var pktConn net.PacketConn
|
||||
|
||||
switch c := conn.(type) {
|
||||
case *internet.PacketConnWrapper:
|
||||
pktConn = c.PacketConn
|
||||
case *cnc.Connection:
|
||||
pktConn = &internet.FakePacketConn{Conn: c}
|
||||
default:
|
||||
panic(reflect.TypeOf(c))
|
||||
}
|
||||
|
||||
return pktConn, nil
|
||||
}
|
||||
|
||||
var pktConn net.PacketConn
|
||||
var udpAddr *net.UDPAddr
|
||||
var index int
|
||||
|
||||
if len(quicParams.UdpHop.Ports) > 0 {
|
||||
index = rand.Intn(len(quicParams.UdpHop.Ports))
|
||||
dest.Port = net.Port(quicParams.UdpHop.Ports[index])
|
||||
}
|
||||
|
||||
raw, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
|
||||
if err != nil {
|
||||
@@ -243,10 +213,6 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
||||
panic(reflect.TypeOf(c))
|
||||
}
|
||||
|
||||
if len(quicParams.UdpHop.Ports) > 0 {
|
||||
pktConn = udphop.NewUDPHopPacketConn(udphop.ToAddrs(udpAddr.IP, quicParams.UdpHop.Ports), time.Duration(quicParams.UdpHop.IntervalMin)*time.Second, time.Duration(quicParams.UdpHop.IntervalMax)*time.Second, udpHopDialer, pktConn, index)
|
||||
}
|
||||
|
||||
if streamSettings.UdpmaskManager != nil {
|
||||
newConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(pktConn)
|
||||
if err != nil {
|
||||
|
||||
@@ -493,7 +493,6 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet
|
||||
if quicParams == nil {
|
||||
quicParams = &internet.QuicParams{
|
||||
BbrProfile: string(bbr.ProfileStandard),
|
||||
UdpHop: &internet.UdpHop{},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,8 +63,8 @@ func New(opts ...Option) (*Reader, *Writer) {
|
||||
}
|
||||
|
||||
return &Reader{
|
||||
pipe: p,
|
||||
}, &Writer{
|
||||
pipe: p,
|
||||
}
|
||||
pipe: p,
|
||||
}, &Writer{
|
||||
pipe: p,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user