github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/proxy/shadowsocks2022/client.go (about)

     1  package shadowsocks2022
     2  
     3  import (
     4  	"context"
     5  	gonet "net"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/v2fly/v2ray-core/v5/common"
    10  	"github.com/v2fly/v2ray-core/v5/common/buf"
    11  	"github.com/v2fly/v2ray-core/v5/common/environment"
    12  	"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
    13  	"github.com/v2fly/v2ray-core/v5/common/net"
    14  	"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
    15  	"github.com/v2fly/v2ray-core/v5/common/retry"
    16  	"github.com/v2fly/v2ray-core/v5/common/session"
    17  	"github.com/v2fly/v2ray-core/v5/common/signal"
    18  	"github.com/v2fly/v2ray-core/v5/common/task"
    19  	"github.com/v2fly/v2ray-core/v5/transport"
    20  	"github.com/v2fly/v2ray-core/v5/transport/internet"
    21  	"github.com/v2fly/v2ray-core/v5/transport/internet/udp"
    22  )
    23  
    24  type Client struct {
    25  	config *ClientConfig
    26  	ctx    context.Context
    27  }
    28  
    29  const UDPConnectionState = "UDPConnectionState"
    30  
    31  type ClientUDPConnState struct {
    32  	session  *ClientUDPSession
    33  	initOnce *sync.Once
    34  }
    35  
    36  func (c *ClientUDPConnState) GetOrCreateSession(create func() (*ClientUDPSession, error)) (*ClientUDPSession, error) {
    37  	var errOuter error
    38  	c.initOnce.Do(func() {
    39  		sessionState, err := create()
    40  		if err != nil {
    41  			errOuter = newError("failed to create UDP session").Base(err)
    42  			return
    43  		}
    44  		c.session = sessionState
    45  	})
    46  	if errOuter != nil {
    47  		return nil, newError("failed to initialize UDP State").Base(errOuter)
    48  	}
    49  	return c.session, nil
    50  }
    51  
    52  func NewClientUDPConnState() (*ClientUDPConnState, error) {
    53  	return &ClientUDPConnState{initOnce: &sync.Once{}}, nil
    54  }
    55  
    56  func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
    57  	outbound := session.OutboundFromContext(ctx)
    58  	if outbound == nil || !outbound.Target.IsValid() {
    59  		return newError("target not specified")
    60  	}
    61  	destination := outbound.Target
    62  	network := destination.Network
    63  
    64  	keyDerivation := newBLAKE3KeyDerivation()
    65  	var method Method
    66  	switch c.config.Method {
    67  	case "2022-blake3-aes-128-gcm":
    68  		method = newAES128GCMMethod()
    69  	case "2022-blake3-aes-256-gcm":
    70  		method = newAES256GCMMethod()
    71  	default:
    72  		return newError("unknown method: ", c.config.Method)
    73  	}
    74  
    75  	effectivePsk := c.config.Psk
    76  
    77  	ctx, cancel := context.WithCancel(ctx)
    78  	timer := signal.CancelAfterInactivity(ctx, cancel, time.Minute)
    79  
    80  	if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil {
    81  		udpSession, err := c.getUDPSession(c.ctx, network, dialer, method, keyDerivation)
    82  		if err != nil {
    83  			return newError("failed to get UDP udpSession").Base(err)
    84  		}
    85  		requestDone := func() error {
    86  			return udp.CopyPacketConn(udpSession, packetConn, udp.UpdateActivity(timer))
    87  		}
    88  		responseDone := func() error {
    89  			return udp.CopyPacketConn(packetConn, udpSession, udp.UpdateActivity(timer))
    90  		}
    91  		responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer))
    92  		if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil {
    93  			return newError("connection ends").Base(err)
    94  		}
    95  		return nil
    96  	}
    97  
    98  	if network == net.Network_TCP {
    99  		var conn internet.Connection
   100  		err := retry.ExponentialBackoff(5, 100).On(func() error {
   101  			dest := net.TCPDestination(c.config.Address.AsAddress(), net.Port(c.config.Port))
   102  			dest.Network = network
   103  			rawConn, err := dialer.Dial(ctx, dest)
   104  			if err != nil {
   105  				return err
   106  			}
   107  			conn = rawConn
   108  
   109  			return nil
   110  		})
   111  		if err != nil {
   112  			return newError("failed to find an available destination").AtWarning().Base(err)
   113  		}
   114  		newError("tunneling request to ", destination, " via ", network, ":", net.TCPDestination(c.config.Address.AsAddress(), net.Port(c.config.Port)).NetAddr()).WriteToLog(session.ExportIDToError(ctx))
   115  		defer conn.Close()
   116  
   117  		request := &TCPRequest{
   118  			keyDerivation: keyDerivation,
   119  			method:        method,
   120  		}
   121  		TCPRequestBuffer := buf.New()
   122  		defer TCPRequestBuffer.Release()
   123  		err = request.EncodeTCPRequestHeader(effectivePsk, c.config.Ipsk, destination.Address,
   124  			int(destination.Port), nil, TCPRequestBuffer)
   125  		if err != nil {
   126  			return newError("failed to encode TCP request header").Base(err)
   127  		}
   128  		_, err = conn.Write(TCPRequestBuffer.Bytes())
   129  		if err != nil {
   130  			return newError("failed to write TCP request header").Base(err)
   131  		}
   132  		requestDone := func() error {
   133  			encodedWriter := request.CreateClientC2SWriter(conn)
   134  			return buf.Copy(link.Reader, encodedWriter, buf.UpdateActivity(timer))
   135  		}
   136  		responseDone := func() error {
   137  			err = request.DecodeTCPResponseHeader(effectivePsk, conn)
   138  			if err != nil {
   139  				return newError("failed to decode TCP response header").Base(err)
   140  			}
   141  			if err = request.CheckC2SConnectionConstraint(); err != nil {
   142  				return newError("C2S connection constraint violation").Base(err)
   143  			}
   144  			initialPayload := buf.NewWithSize(65535)
   145  			encodedReader, err := request.CreateClientS2CReader(conn, initialPayload)
   146  			if err != nil {
   147  				return newError("failed to create client S2C reader").Base(err)
   148  			}
   149  			err = link.Writer.WriteMultiBuffer(buf.MultiBuffer{initialPayload})
   150  			if err != nil {
   151  				return newError("failed to write initial payload").Base(err)
   152  			}
   153  			return buf.Copy(encodedReader, link.Writer, buf.UpdateActivity(timer))
   154  		}
   155  		responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer))
   156  		if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil {
   157  			return newError("connection ends").Base(err)
   158  		}
   159  		return nil
   160  	} else {
   161  		udpSession, err := c.getUDPSession(c.ctx, network, dialer, method, keyDerivation)
   162  		if err != nil {
   163  			return newError("failed to get UDP udpSession").Base(err)
   164  		}
   165  		monoDestUDPConn := udp.NewMonoDestUDPConn(udpSession, &gonet.UDPAddr{IP: destination.Address.IP(), Port: int(destination.Port)})
   166  		requestDone := func() error {
   167  			return buf.Copy(link.Reader, monoDestUDPConn, buf.UpdateActivity(timer))
   168  		}
   169  		responseDone := func() error {
   170  			return buf.Copy(monoDestUDPConn, link.Writer, buf.UpdateActivity(timer))
   171  		}
   172  		responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer))
   173  		if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil {
   174  			return newError("connection ends").Base(err)
   175  		}
   176  		return nil
   177  	}
   178  }
   179  
   180  func (c *Client) getUDPSession(ctx context.Context, network net.Network, dialer internet.Dialer, method Method, keyDerivation *BLAKE3KeyDerivation) (internet.AbstractPacketConn, error) {
   181  	storage := envctx.EnvironmentFromContext(ctx).(environment.ProxyEnvironment).TransientStorage()
   182  	clientUDPStateIfce, err := storage.Get(ctx, UDPConnectionState)
   183  	if err != nil {
   184  		return nil, newError("failed to get UDP connection state").Base(err)
   185  	}
   186  	clientUDPState, ok := clientUDPStateIfce.(*ClientUDPConnState)
   187  	if !ok {
   188  		return nil, newError("failed to cast UDP connection state")
   189  	}
   190  
   191  	sessionState, err := clientUDPState.GetOrCreateSession(func() (*ClientUDPSession, error) {
   192  		var conn internet.Connection
   193  		err := retry.ExponentialBackoff(5, 100).On(func() error {
   194  			dest := net.TCPDestination(c.config.Address.AsAddress(), net.Port(c.config.Port))
   195  			dest.Network = network
   196  			rawConn, err := dialer.Dial(ctx, dest)
   197  			if err != nil {
   198  				return err
   199  			}
   200  			conn = rawConn
   201  
   202  			return nil
   203  		})
   204  		if err != nil {
   205  			return nil, newError("failed to find an available destination").AtWarning().Base(err)
   206  		}
   207  		newError("creating udp session to ", network, ":", c.config.Address).WriteToLog(session.ExportIDToError(ctx))
   208  		packetProcessor, err := method.GetUDPClientProcessor(c.config.Ipsk, c.config.Psk, keyDerivation)
   209  		if err != nil {
   210  			return nil, newError("failed to create UDP client packet processor").Base(err)
   211  		}
   212  		return NewClientUDPSession(ctx, conn, packetProcessor), nil
   213  	})
   214  	if err != nil {
   215  		return nil, newError("failed to create UDP session").Base(err)
   216  	}
   217  	sessionConn, err := sessionState.NewSessionConn()
   218  	if err != nil {
   219  		return nil, newError("failed to create UDP session connection").Base(err)
   220  	}
   221  	return sessionConn, nil
   222  }
   223  
   224  func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
   225  	storage := envctx.EnvironmentFromContext(ctx).(environment.ProxyEnvironment).TransientStorage()
   226  
   227  	udpState, err := NewClientUDPConnState()
   228  	if err != nil {
   229  		return nil, newError("failed to create UDP connection state").Base(err)
   230  	}
   231  	storage.Put(ctx, UDPConnectionState, udpState)
   232  
   233  	return &Client{
   234  		config: config,
   235  		ctx:    ctx,
   236  	}, nil
   237  }
   238  
   239  func init() {
   240  	common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   241  		clientConfig, ok := config.(*ClientConfig)
   242  		if !ok {
   243  			return nil, newError("not a ClientConfig")
   244  		}
   245  		return NewClient(ctx, clientConfig)
   246  	}))
   247  }