github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/proxy/shadowsocks_2022/outbound.go (about)

     1  package shadowsocks_2022
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	shadowsocks "github.com/sagernet/sing-shadowsocks"
     8  	"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
     9  	C "github.com/sagernet/sing/common"
    10  	B "github.com/sagernet/sing/common/buf"
    11  	"github.com/sagernet/sing/common/bufio"
    12  	N "github.com/sagernet/sing/common/network"
    13  	"github.com/sagernet/sing/common/uot"
    14  	"github.com/xtls/xray-core/common"
    15  	"github.com/xtls/xray-core/common/buf"
    16  	"github.com/xtls/xray-core/common/net"
    17  	"github.com/xtls/xray-core/common/session"
    18  	"github.com/xtls/xray-core/common/singbridge"
    19  	"github.com/xtls/xray-core/transport"
    20  	"github.com/xtls/xray-core/transport/internet"
    21  )
    22  
    23  func init() {
    24  	common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
    25  		return NewClient(ctx, config.(*ClientConfig))
    26  	}))
    27  }
    28  
    29  type Outbound struct {
    30  	ctx       context.Context
    31  	server    net.Destination
    32  	method    shadowsocks.Method
    33  	uotClient *uot.Client
    34  }
    35  
    36  func NewClient(ctx context.Context, config *ClientConfig) (*Outbound, error) {
    37  	o := &Outbound{
    38  		ctx: ctx,
    39  		server: net.Destination{
    40  			Address: config.Address.AsAddress(),
    41  			Port:    net.Port(config.Port),
    42  			Network: net.Network_TCP,
    43  		},
    44  	}
    45  	if C.Contains(shadowaead_2022.List, config.Method) {
    46  		if config.Key == "" {
    47  			return nil, newError("missing psk")
    48  		}
    49  		method, err := shadowaead_2022.NewWithPassword(config.Method, config.Key, nil)
    50  		if err != nil {
    51  			return nil, newError("create method").Base(err)
    52  		}
    53  		o.method = method
    54  	} else {
    55  		return nil, newError("unknown method ", config.Method)
    56  	}
    57  	if config.UdpOverTcp {
    58  		o.uotClient = &uot.Client{Version: uint8(config.UdpOverTcpVersion)}
    59  	}
    60  	return o, nil
    61  }
    62  
    63  func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
    64  	var inboundConn net.Conn
    65  	inbound := session.InboundFromContext(ctx)
    66  	if inbound != nil {
    67  		inboundConn = inbound.Conn
    68  	}
    69  
    70  	outbounds := session.OutboundsFromContext(ctx)
    71  	ob := outbounds[len(outbounds) - 1]
    72  	if !ob.Target.IsValid() {
    73  		return newError("target not specified")
    74  	}
    75  	ob.Name = "shadowsocks-2022"
    76  	ob.CanSpliceCopy = 3
    77  	destination := ob.Target
    78  	network := destination.Network
    79  
    80  	newError("tunneling request to ", destination, " via ", o.server.NetAddr()).WriteToLog(session.ExportIDToError(ctx))
    81  
    82  	serverDestination := o.server
    83  	if o.uotClient != nil {
    84  		serverDestination.Network = net.Network_TCP
    85  	} else {
    86  		serverDestination.Network = network
    87  	}
    88  	connection, err := dialer.Dial(ctx, serverDestination)
    89  	if err != nil {
    90  		return newError("failed to connect to server").Base(err)
    91  	}
    92  
    93  	if session.TimeoutOnlyFromContext(ctx) {
    94  		ctx, _ = context.WithCancel(context.Background())
    95  	}
    96  
    97  	if network == net.Network_TCP {
    98  		serverConn := o.method.DialEarlyConn(connection, singbridge.ToSocksaddr(destination))
    99  		var handshake bool
   100  		if timeoutReader, isTimeoutReader := link.Reader.(buf.TimeoutReader); isTimeoutReader {
   101  			mb, err := timeoutReader.ReadMultiBufferTimeout(time.Millisecond * 100)
   102  			if err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
   103  				return newError("read payload").Base(err)
   104  			}
   105  			payload := B.New()
   106  			for {
   107  				payload.Reset()
   108  				nb, n := buf.SplitBytes(mb, payload.FreeBytes())
   109  				if n > 0 {
   110  					payload.Truncate(n)
   111  					_, err = serverConn.Write(payload.Bytes())
   112  					if err != nil {
   113  						payload.Release()
   114  						return newError("write payload").Base(err)
   115  					}
   116  					handshake = true
   117  				}
   118  				if nb.IsEmpty() {
   119  					break
   120  				}
   121  				mb = nb
   122  			}
   123  			payload.Release()
   124  		}
   125  		if !handshake {
   126  			_, err = serverConn.Write(nil)
   127  			if err != nil {
   128  				return newError("client handshake").Base(err)
   129  			}
   130  		}
   131  		return singbridge.CopyConn(ctx, inboundConn, link, serverConn)
   132  	} else {
   133  		var packetConn N.PacketConn
   134  		if pc, isPacketConn := inboundConn.(N.PacketConn); isPacketConn {
   135  			packetConn = pc
   136  		} else if nc, isNetPacket := inboundConn.(net.PacketConn); isNetPacket {
   137  			packetConn = bufio.NewPacketConn(nc)
   138  		} else {
   139  			packetConn = &singbridge.PacketConnWrapper{
   140  				Reader: link.Reader,
   141  				Writer: link.Writer,
   142  				Conn:   inboundConn,
   143  				Dest:   destination,
   144  			}
   145  		}
   146  
   147  		if o.uotClient != nil {
   148  			uConn, err := o.uotClient.DialEarlyConn(o.method.DialEarlyConn(connection, uot.RequestDestination(o.uotClient.Version)), false, singbridge.ToSocksaddr(destination))
   149  			if err != nil {
   150  				return err
   151  			}
   152  			return singbridge.ReturnError(bufio.CopyPacketConn(ctx, packetConn, uConn))
   153  		} else {
   154  			serverConn := o.method.DialPacketConn(connection)
   155  			return singbridge.ReturnError(bufio.CopyPacketConn(ctx, packetConn, serverConn))
   156  		}
   157  	}
   158  }