mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-09-22 22:58:03 +03:00
https://github.com/XTLS/Xray-core/pull/6327#issuecomment-5645958010 https://github.com/XTLS/Xray-core/pull/6754#issuecomment-5720818254 https://github.com/XTLS/Xray-core/pull/6754#issuecomment-5751585906
423 lines
10 KiB
Go
423 lines
10 KiB
Go
package finalmask
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/xtls/xray-core/common/buf"
|
|
"github.com/xtls/xray-core/common/errors"
|
|
"github.com/xtls/xray-core/common/net"
|
|
)
|
|
|
|
type Dialer struct {
|
|
DialTCP func(net.Destination) (net.Conn, error)
|
|
DialUDP func(net.Destination) (net.Conn, error)
|
|
}
|
|
|
|
type ListenConfig struct {
|
|
Listen func(net.Addr) (net.Listener, error)
|
|
ListenPacket func(net.Addr) (net.PacketConn, error)
|
|
}
|
|
|
|
type TCPMask interface {
|
|
WrapConnClient(net.Conn, *net.Destination, *Dialer) (net.Conn, error)
|
|
WrapConnServer(net.Conn) (net.Conn, error)
|
|
// Listen(net.Listener) (net.Listener, error)
|
|
}
|
|
|
|
type UDPMask interface {
|
|
WrapPacketConnClient(net.PacketConn, *net.Destination, *Dialer) (net.PacketConn, error)
|
|
WrapPacketConnServer(net.PacketConn, net.Addr, *ListenConfig) (net.PacketConn, error)
|
|
}
|
|
|
|
type FinalMask struct {
|
|
tcpMasks []TCPMask
|
|
udpMasks []UDPMask
|
|
dialTCP func(context.Context, net.Destination) (net.Conn, error)
|
|
listen func(context.Context, net.Addr) (net.Listener, error)
|
|
dialUDP func(context.Context, net.Destination) (net.PacketConn, net.Addr, error)
|
|
listenPacket func(context.Context, net.Addr) (net.PacketConn, error)
|
|
}
|
|
|
|
func NewFinalMask(tcpMasks []TCPMask, udpMasks []UDPMask, dialTCP func(context.Context, net.Destination) (net.Conn, error), listen func(context.Context, net.Addr) (net.Listener, error), dialUDP func(context.Context, net.Destination) (net.PacketConn, net.Addr, error), listenPacket func(context.Context, net.Addr) (net.PacketConn, error)) *FinalMask {
|
|
slices.Reverse(tcpMasks)
|
|
slices.Reverse(udpMasks)
|
|
return &FinalMask{
|
|
tcpMasks: tcpMasks,
|
|
udpMasks: udpMasks,
|
|
dialTCP: dialTCP,
|
|
dialUDP: dialUDP,
|
|
listen: listen,
|
|
listenPacket: listenPacket,
|
|
}
|
|
}
|
|
|
|
func (fm *FinalMask) DialTCP(ctx context.Context, dest net.Destination) (net.Conn, error) {
|
|
if len(fm.tcpMasks) == 0 {
|
|
return fm.dialTCP(ctx, dest)
|
|
}
|
|
for i := range fm.tcpMasks {
|
|
if i > 0 {
|
|
if _, ok := fm.tcpMasks[i].(interface{ HandleDial() }); ok {
|
|
return nil, fmt.Errorf("incorrect index: %d %T", i, fm.tcpMasks[i])
|
|
}
|
|
}
|
|
}
|
|
var conn net.Conn
|
|
var err error
|
|
if _, ok := fm.tcpMasks[0].(interface{ HandleDial() }); !ok {
|
|
conn, err = fm.dialTCP(ctx, dest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
dialer := &Dialer{
|
|
DialTCP: func(dest net.Destination) (net.Conn, error) {
|
|
return fm.dialTCP(ctx, dest)
|
|
},
|
|
DialUDP: func(dest net.Destination) (net.Conn, error) {
|
|
conn, addr, err := fm.dialUDP(ctx, dest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &PacketConnWrapper{PacketConn: conn, udpAddr: addr}, err
|
|
},
|
|
}
|
|
for i := range fm.tcpMasks {
|
|
var newConn net.Conn
|
|
newConn, err = fm.tcpMasks[i].WrapConnClient(conn, &dest, dialer)
|
|
if err != nil {
|
|
_ = conn.Close()
|
|
return nil, err
|
|
}
|
|
conn = newConn
|
|
}
|
|
return conn, nil
|
|
}
|
|
|
|
func (fm *FinalMask) Listen(ctx context.Context, addr net.Addr) (net.Listener, error) {
|
|
if len(fm.tcpMasks) == 0 {
|
|
return fm.listen(ctx, addr)
|
|
}
|
|
off := 0
|
|
listener, err := fm.listen(ctx, addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := range fm.tcpMasks {
|
|
if _, ok := fm.tcpMasks[i].(interface {
|
|
Listen(net.Listener) (net.Listener, error)
|
|
}); ok {
|
|
if i-off == 0 {
|
|
l, err := fm.tcpMasks[i].(interface {
|
|
Listen(net.Listener) (net.Listener, error)
|
|
}).Listen(listener)
|
|
if err != nil {
|
|
listener.Close()
|
|
return nil, err
|
|
}
|
|
listener = l
|
|
} else {
|
|
l, err := fm.tcpMasks[i].(interface {
|
|
Listen(net.Listener) (net.Listener, error)
|
|
}).Listen(&TCPListener{Listener: listener, tcpMasks: fm.tcpMasks[off:i]})
|
|
if err != nil {
|
|
listener.Close()
|
|
return nil, err
|
|
}
|
|
listener = l
|
|
}
|
|
off = i + 1
|
|
}
|
|
}
|
|
if off < len(fm.tcpMasks) {
|
|
return &TCPListener{Listener: listener, tcpMasks: fm.tcpMasks[off:]}, nil
|
|
}
|
|
return listener, nil
|
|
}
|
|
|
|
func (fm *FinalMask) DialUDP(ctx context.Context, dest net.Destination) (net.Conn, error) {
|
|
if len(fm.udpMasks) == 0 {
|
|
conn, addr, err := fm.dialUDP(ctx, dest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &PacketConnWrapper{PacketConn: conn, udpAddr: addr}, nil
|
|
}
|
|
for i := range fm.udpMasks {
|
|
if i > 0 {
|
|
if _, ok := fm.udpMasks[i].(interface{ HandleDial() }); ok {
|
|
return nil, fmt.Errorf("incorrect index: %d %T", i, fm.udpMasks[i])
|
|
}
|
|
}
|
|
}
|
|
var conn net.PacketConn
|
|
var addr net.Addr
|
|
var err error
|
|
if _, ok := fm.udpMasks[0].(interface{ HandleDial() }); !ok {
|
|
conn, addr, err = fm.dialUDP(ctx, dest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
dialer := &Dialer{
|
|
DialTCP: func(dest net.Destination) (net.Conn, error) {
|
|
return fm.dialTCP(ctx, dest)
|
|
},
|
|
DialUDP: func(dest net.Destination) (net.Conn, error) {
|
|
conn, addr, err := fm.dialUDP(ctx, dest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &PacketConnWrapper{PacketConn: conn, udpAddr: addr}, err
|
|
},
|
|
}
|
|
var sizes []int
|
|
var conns []net.PacketConn
|
|
for i := range fm.udpMasks {
|
|
var newConn net.PacketConn
|
|
if _, ok := fm.udpMasks[i].(interface{ HeaderConn() }); ok {
|
|
newConn, err = fm.udpMasks[i].WrapPacketConnClient(nil, nil, nil)
|
|
if err != nil {
|
|
_ = conn.Close()
|
|
return nil, err
|
|
}
|
|
sizes = append(sizes, newConn.(interface{ Size() int }).Size())
|
|
conns = append(conns, newConn)
|
|
} else {
|
|
if len(conns) > 0 {
|
|
conn = &headerManagerConn{PacketConn: conn, sizes: sizes, conns: conns}
|
|
sizes = nil
|
|
conns = nil
|
|
}
|
|
newConn, err = fm.udpMasks[i].WrapPacketConnClient(conn, &dest, dialer)
|
|
if err != nil {
|
|
_ = conn.Close()
|
|
return nil, err
|
|
}
|
|
conn = newConn
|
|
}
|
|
}
|
|
if len(conns) > 0 {
|
|
conn = &headerManagerConn{PacketConn: conn, sizes: sizes, conns: conns}
|
|
sizes = nil
|
|
conns = nil
|
|
}
|
|
if addr == nil {
|
|
addr = &net.UDPAddr{IP: []byte{0, 0, 0, 0}}
|
|
}
|
|
return &PacketConnWrapper{PacketConn: conn, udpAddr: addr}, nil
|
|
}
|
|
|
|
func (fm *FinalMask) ListenPacket(ctx context.Context, addr net.Addr) (net.PacketConn, error) {
|
|
if len(fm.udpMasks) == 0 {
|
|
return fm.listenPacket(ctx, addr)
|
|
}
|
|
for i := range fm.udpMasks {
|
|
if i > 0 {
|
|
if _, ok := fm.udpMasks[i].(interface{ HandleListen() }); ok {
|
|
return nil, fmt.Errorf("incorrect index: %d %T", i, fm.udpMasks[i])
|
|
}
|
|
}
|
|
}
|
|
var conn net.PacketConn
|
|
var err error
|
|
if _, ok := fm.udpMasks[0].(interface{ HandleListen() }); !ok {
|
|
conn, err = fm.listenPacket(ctx, addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
lc := &ListenConfig{
|
|
Listen: func(addr net.Addr) (net.Listener, error) { return fm.listen(ctx, addr) },
|
|
ListenPacket: func(addr net.Addr) (net.PacketConn, error) { return fm.listenPacket(ctx, addr) },
|
|
}
|
|
var sizes []int
|
|
var conns []net.PacketConn
|
|
for i := range fm.udpMasks {
|
|
var newConn net.PacketConn
|
|
if _, ok := fm.udpMasks[i].(interface{ HeaderConn() }); ok {
|
|
newConn, err = fm.udpMasks[i].WrapPacketConnServer(nil, nil, nil)
|
|
if err != nil {
|
|
_ = conn.Close()
|
|
return nil, err
|
|
}
|
|
sizes = append(sizes, newConn.(interface{ Size() int }).Size())
|
|
conns = append(conns, newConn)
|
|
} else {
|
|
if len(conns) > 0 {
|
|
conn = &headerManagerConn{PacketConn: conn, sizes: sizes, conns: conns}
|
|
sizes = nil
|
|
conns = nil
|
|
}
|
|
newConn, err = fm.udpMasks[i].WrapPacketConnServer(conn, addr, lc)
|
|
if err != nil {
|
|
_ = conn.Close()
|
|
return nil, err
|
|
}
|
|
conn = newConn
|
|
}
|
|
}
|
|
if len(conns) > 0 {
|
|
conn = &headerManagerConn{PacketConn: conn, sizes: sizes, conns: conns}
|
|
sizes = nil
|
|
conns = nil
|
|
}
|
|
return conn, nil
|
|
}
|
|
|
|
const (
|
|
UDPSize = 4096
|
|
)
|
|
|
|
type PacketConnWrapper struct {
|
|
net.PacketConn
|
|
udpAddr net.Addr
|
|
}
|
|
|
|
func (c *PacketConnWrapper) RemoteAddr() net.Addr {
|
|
return c.udpAddr
|
|
}
|
|
|
|
func (c *PacketConnWrapper) Read(b []byte) (n int, err error) {
|
|
n, _, err = c.PacketConn.ReadFrom(b)
|
|
return
|
|
}
|
|
|
|
func (c *PacketConnWrapper) Write(b []byte) (n int, err error) {
|
|
return c.PacketConn.WriteTo(b, c.udpAddr)
|
|
}
|
|
|
|
type headerManagerConn struct {
|
|
net.PacketConn
|
|
|
|
sizes []int
|
|
conns []net.PacketConn
|
|
}
|
|
|
|
func (c *headerManagerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|
b := p
|
|
if len(b) < UDPSize {
|
|
buf := buf.New()
|
|
buf.Resize(0, UDPSize)
|
|
b = buf.Bytes()
|
|
defer buf.Release()
|
|
}
|
|
|
|
for {
|
|
n, addr, err = c.PacketConn.ReadFrom(b)
|
|
if err != nil {
|
|
return n, addr, err
|
|
}
|
|
buf := b[:n]
|
|
|
|
sum := 0
|
|
for _, size := range c.sizes {
|
|
sum += size
|
|
}
|
|
|
|
if n < sum {
|
|
errors.LogError(context.Background(), "[mask] drop packet from ", addr, " with size ", n)
|
|
continue
|
|
}
|
|
|
|
for i := range c.conns {
|
|
n, _, err = c.conns[i].ReadFrom(buf)
|
|
if err != nil {
|
|
errors.LogErrorInner(context.Background(), err, "[mask] drop packet from ", addr, " with size ", n)
|
|
break
|
|
}
|
|
buf = buf[c.sizes[i] : n+c.sizes[i]]
|
|
}
|
|
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
return copy(p, buf), addr, nil
|
|
}
|
|
}
|
|
|
|
func (c *headerManagerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|
buf := buf.New()
|
|
buf.Resize(0, UDPSize)
|
|
b := buf.Bytes()
|
|
defer buf.Release()
|
|
|
|
sum := 0
|
|
for _, size := range c.sizes {
|
|
sum += size
|
|
}
|
|
|
|
if sum+len(p) > UDPSize {
|
|
errors.LogError(context.Background(), "[mask] drop packet to ", addr, " with size ", len(p))
|
|
return 0, nil
|
|
}
|
|
|
|
n = copy(b[sum:], p)
|
|
|
|
for i := len(c.conns) - 1; i >= 0; i-- {
|
|
n, err = c.conns[i].WriteTo(b[sum-c.sizes[i]:n+sum], nil)
|
|
if err != nil {
|
|
errors.LogErrorInner(context.Background(), err, "[mask] drop packet to ", addr, " with size ", len(p))
|
|
return 0, nil
|
|
}
|
|
sum -= c.sizes[i]
|
|
}
|
|
|
|
if n > UDPSize {
|
|
errors.LogError(context.Background(), "[mask] drop packet to ", addr, " with size ", len(p))
|
|
return 0, nil
|
|
}
|
|
|
|
_, err = c.PacketConn.WriteTo(b[:n], addr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return len(p), nil
|
|
}
|
|
|
|
type TCPListener struct {
|
|
net.Listener
|
|
tcpMasks []TCPMask
|
|
}
|
|
|
|
func (l *TCPListener) Accept() (net.Conn, error) {
|
|
conn, err := l.Listener.Accept()
|
|
if err != nil {
|
|
return conn, err
|
|
}
|
|
|
|
for i := range l.tcpMasks {
|
|
var newConn net.Conn
|
|
newConn, err = l.tcpMasks[i].WrapConnServer(conn)
|
|
if err != nil {
|
|
_ = conn.Close()
|
|
return nil, err
|
|
}
|
|
conn = newConn
|
|
}
|
|
return conn, nil
|
|
}
|
|
|
|
type TcpMaskConn interface {
|
|
TcpMaskConn()
|
|
RawConn() net.Conn
|
|
Splice() bool
|
|
}
|
|
|
|
func UnwrapTcpMask(conn net.Conn) net.Conn {
|
|
for {
|
|
if v, ok := conn.(TcpMaskConn); ok {
|
|
if !v.Splice() {
|
|
return conn
|
|
}
|
|
conn = v.RawConn()
|
|
} else {
|
|
return conn
|
|
}
|
|
}
|
|
}
|