mirror of https://github.com/XTLS/Xray-core
				
				
				
			
		
			
				
	
	
		
			158 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
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/sagernet/sing/common/uot"
 | 
						|
	"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/session"
 | 
						|
	"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
 | 
						|
	uotClient *uot.Client
 | 
						|
}
 | 
						|
 | 
						|
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, newError("missing psk")
 | 
						|
		}
 | 
						|
		method, err := shadowaead_2022.NewWithPassword(config.Method, config.Key, nil)
 | 
						|
		if err != nil {
 | 
						|
			return nil, newError("create method").Base(err)
 | 
						|
		}
 | 
						|
		o.method = method
 | 
						|
	} else {
 | 
						|
		return nil, newError("unknown method ", config.Method)
 | 
						|
	}
 | 
						|
	if config.UdpOverTcp {
 | 
						|
		o.uotClient = &uot.Client{Version: uint8(config.UdpOverTcpVersion)}
 | 
						|
	}
 | 
						|
	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
 | 
						|
		inbound.SetCanSpliceCopy(3)
 | 
						|
	}
 | 
						|
 | 
						|
	outbound := session.OutboundFromContext(ctx)
 | 
						|
	if outbound == nil || !outbound.Target.IsValid() {
 | 
						|
		return newError("target not specified")
 | 
						|
	}
 | 
						|
	outbound.Name = "shadowsocks-2022"
 | 
						|
	destination := outbound.Target
 | 
						|
	network := destination.Network
 | 
						|
 | 
						|
	newError("tunneling request to ", destination, " via ", o.server.NetAddr()).WriteToLog(session.ExportIDToError(ctx))
 | 
						|
 | 
						|
	serverDestination := o.server
 | 
						|
	if o.uotClient != nil {
 | 
						|
		serverDestination.Network = net.Network_TCP
 | 
						|
	} else {
 | 
						|
		serverDestination.Network = network
 | 
						|
	}
 | 
						|
	connection, err := dialer.Dial(ctx, serverDestination)
 | 
						|
	if err != nil {
 | 
						|
		return newError("failed to connect to server").Base(err)
 | 
						|
	}
 | 
						|
 | 
						|
	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 newError("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 newError("write payload").Base(err)
 | 
						|
					}
 | 
						|
					handshake = true
 | 
						|
				}
 | 
						|
				if nb.IsEmpty() {
 | 
						|
					break
 | 
						|
				}
 | 
						|
				mb = nb
 | 
						|
			}
 | 
						|
			payload.Release()
 | 
						|
		}
 | 
						|
		if !handshake {
 | 
						|
			_, err = serverConn.Write(nil)
 | 
						|
			if err != nil {
 | 
						|
				return newError("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,
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if o.uotClient != nil {
 | 
						|
			uConn, err := o.uotClient.DialEarlyConn(o.method.DialEarlyConn(connection, uot.RequestDestination(o.uotClient.Version)), false, singbridge.ToSocksaddr(destination))
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			return singbridge.ReturnError(bufio.CopyPacketConn(ctx, packetConn, uConn))
 | 
						|
		} else {
 | 
						|
			serverConn := o.method.DialPacketConn(connection)
 | 
						|
			return singbridge.ReturnError(bufio.CopyPacketConn(ctx, packetConn, serverConn))
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |