2023-11-03 16:10:11 -04:00
package shadowsocks2022
import (
"context"
2023-11-18 19:42:20 -05:00
gonet "net"
"sync"
"time"
2023-11-03 16:10:11 -04:00
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/buf"
2023-11-18 18:09:26 -05:00
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
2023-11-03 16:10:11 -04:00
"github.com/v2fly/v2ray-core/v5/common/net"
2023-11-18 18:09:26 -05:00
"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
2023-11-03 16:10:11 -04:00
"github.com/v2fly/v2ray-core/v5/common/retry"
"github.com/v2fly/v2ray-core/v5/common/session"
"github.com/v2fly/v2ray-core/v5/common/signal"
"github.com/v2fly/v2ray-core/v5/common/task"
"github.com/v2fly/v2ray-core/v5/transport"
"github.com/v2fly/v2ray-core/v5/transport/internet"
2023-11-18 18:09:26 -05:00
"github.com/v2fly/v2ray-core/v5/transport/internet/udp"
2023-11-03 16:10:11 -04:00
)
type Client struct {
config * ClientConfig
ctx context . Context
}
2023-11-18 18:09:26 -05:00
const UDPConnectionState = "UDPConnectionState"
2023-11-03 16:10:11 -04:00
2023-11-18 18:09:26 -05:00
type ClientUDPConnState struct {
session * ClientUDPSession
initOnce * sync . Once
}
2023-11-03 16:10:11 -04:00
2023-11-18 18:09:26 -05:00
func ( c * ClientUDPConnState ) GetOrCreateSession ( create func ( ) ( * ClientUDPSession , error ) ) ( * ClientUDPSession , error ) {
2023-11-18 19:42:20 -05:00
var errOuter error
2023-11-18 18:09:26 -05:00
c . initOnce . Do ( func ( ) {
sessionState , err := create ( )
2023-11-03 16:10:11 -04:00
if err != nil {
2023-11-18 19:42:20 -05:00
errOuter = newError ( "failed to create UDP session" ) . Base ( err )
2023-11-18 18:09:26 -05:00
return
2023-11-03 16:10:11 -04:00
}
2023-11-18 18:09:26 -05:00
c . session = sessionState
2023-11-03 16:10:11 -04:00
} )
2023-11-18 19:42:20 -05:00
if errOuter != nil {
return nil , newError ( "failed to initialize UDP State" ) . Base ( errOuter )
2023-11-18 18:09:26 -05:00
}
return c . session , nil
}
func NewClientUDPConnState ( ) ( * ClientUDPConnState , error ) {
return & ClientUDPConnState { initOnce : & sync . Once { } } , nil
}
func ( c * Client ) Process ( ctx context . Context , link * transport . Link , dialer internet . Dialer ) error {
outbound := session . OutboundFromContext ( ctx )
if outbound == nil || ! outbound . Target . IsValid ( ) {
return newError ( "target not specified" )
2023-11-03 16:10:11 -04:00
}
2023-11-18 18:09:26 -05:00
destination := outbound . Target
network := destination . Network
2023-11-03 16:10:11 -04:00
2023-11-18 19:42:20 -05:00
keyDerivation := newBLAKE3KeyDerivation ( )
2023-11-03 16:10:11 -04:00
var method Method
switch c . config . Method {
case "2022-blake3-aes-128-gcm" :
method = newAES128GCMMethod ( )
case "2022-blake3-aes-256-gcm" :
method = newAES256GCMMethod ( )
default :
return newError ( "unknown method: " , c . config . Method )
}
effectivePsk := c . config . Psk
ctx , cancel := context . WithCancel ( ctx )
timer := signal . CancelAfterInactivity ( ctx , cancel , time . Minute )
2023-11-18 18:09:26 -05:00
if packetConn , err := packetaddr . ToPacketAddrConn ( link , destination ) ; err == nil {
udpSession , err := c . getUDPSession ( c . ctx , network , dialer , method , keyDerivation )
if err != nil {
return newError ( "failed to get UDP udpSession" ) . Base ( err )
}
requestDone := func ( ) error {
return udp . CopyPacketConn ( udpSession , packetConn , udp . UpdateActivity ( timer ) )
}
responseDone := func ( ) error {
return udp . CopyPacketConn ( packetConn , udpSession , udp . UpdateActivity ( timer ) )
}
responseDoneAndCloseWriter := task . OnSuccess ( responseDone , task . Close ( link . Writer ) )
if err := task . Run ( ctx , requestDone , responseDoneAndCloseWriter ) ; err != nil {
return newError ( "connection ends" ) . Base ( err )
}
return nil
}
2023-11-03 16:10:11 -04:00
if network == net . Network_TCP {
2023-11-18 18:09:26 -05:00
var conn internet . Connection
err := retry . ExponentialBackoff ( 5 , 100 ) . On ( func ( ) error {
dest := net . TCPDestination ( c . config . Address . AsAddress ( ) , net . Port ( c . config . Port ) )
dest . Network = network
rawConn , err := dialer . Dial ( ctx , dest )
if err != nil {
return err
}
conn = rawConn
return nil
} )
if err != nil {
return newError ( "failed to find an available destination" ) . AtWarning ( ) . Base ( err )
}
2023-11-23 19:40:07 -05:00
newError ( "tunneling request to " , destination , " via " , network , ":" , net . TCPDestination ( c . config . Address . AsAddress ( ) , net . Port ( c . config . Port ) ) . NetAddr ( ) ) . WriteToLog ( session . ExportIDToError ( ctx ) )
2023-11-18 18:09:26 -05:00
defer conn . Close ( )
2023-11-03 16:10:11 -04:00
request := & TCPRequest {
keyDerivation : keyDerivation ,
method : method ,
}
TCPRequestBuffer := buf . New ( )
defer TCPRequestBuffer . Release ( )
err = request . EncodeTCPRequestHeader ( effectivePsk , c . config . Ipsk , destination . Address ,
int ( destination . Port ) , nil , TCPRequestBuffer )
if err != nil {
return newError ( "failed to encode TCP request header" ) . Base ( err )
}
_ , err = conn . Write ( TCPRequestBuffer . Bytes ( ) )
if err != nil {
return newError ( "failed to write TCP request header" ) . Base ( err )
}
requestDone := func ( ) error {
encodedWriter := request . CreateClientC2SWriter ( conn )
return buf . Copy ( link . Reader , encodedWriter , buf . UpdateActivity ( timer ) )
}
responseDone := func ( ) error {
err = request . DecodeTCPResponseHeader ( effectivePsk , conn )
if err != nil {
return newError ( "failed to decode TCP response header" ) . Base ( err )
}
if err = request . CheckC2SConnectionConstraint ( ) ; err != nil {
return newError ( "C2S connection constraint violation" ) . Base ( err )
}
initialPayload := buf . NewWithSize ( 65535 )
encodedReader , err := request . CreateClientS2CReader ( conn , initialPayload )
if err != nil {
return newError ( "failed to create client S2C reader" ) . Base ( err )
}
err = link . Writer . WriteMultiBuffer ( buf . MultiBuffer { initialPayload } )
if err != nil {
return newError ( "failed to write initial payload" ) . Base ( err )
}
return buf . Copy ( encodedReader , link . Writer , buf . UpdateActivity ( timer ) )
}
responseDoneAndCloseWriter := task . OnSuccess ( responseDone , task . Close ( link . Writer ) )
if err := task . Run ( ctx , requestDone , responseDoneAndCloseWriter ) ; err != nil {
return newError ( "connection ends" ) . Base ( err )
}
return nil
} else {
2023-11-18 18:09:26 -05:00
udpSession , err := c . getUDPSession ( c . ctx , network , dialer , method , keyDerivation )
if err != nil {
return newError ( "failed to get UDP udpSession" ) . Base ( err )
}
monoDestUDPConn := udp . NewMonoDestUDPConn ( udpSession , & gonet . UDPAddr { IP : destination . Address . IP ( ) , Port : int ( destination . Port ) } )
requestDone := func ( ) error {
return buf . Copy ( link . Reader , monoDestUDPConn , buf . UpdateActivity ( timer ) )
}
responseDone := func ( ) error {
return buf . Copy ( monoDestUDPConn , link . Writer , buf . UpdateActivity ( timer ) )
}
responseDoneAndCloseWriter := task . OnSuccess ( responseDone , task . Close ( link . Writer ) )
if err := task . Run ( ctx , requestDone , responseDoneAndCloseWriter ) ; err != nil {
return newError ( "connection ends" ) . Base ( err )
}
return nil
2023-11-03 16:10:11 -04:00
}
}
2023-11-18 18:09:26 -05:00
func ( c * Client ) getUDPSession ( ctx context . Context , network net . Network , dialer internet . Dialer , method Method , keyDerivation * BLAKE3KeyDerivation ) ( internet . AbstractPacketConn , error ) {
storage := envctx . EnvironmentFromContext ( ctx ) . ( environment . ProxyEnvironment ) . TransientStorage ( )
clientUDPStateIfce , err := storage . Get ( ctx , UDPConnectionState )
if err != nil {
return nil , newError ( "failed to get UDP connection state" ) . Base ( err )
}
clientUDPState , ok := clientUDPStateIfce . ( * ClientUDPConnState )
if ! ok {
return nil , newError ( "failed to cast UDP connection state" )
}
sessionState , err := clientUDPState . GetOrCreateSession ( func ( ) ( * ClientUDPSession , error ) {
var conn internet . Connection
err := retry . ExponentialBackoff ( 5 , 100 ) . On ( func ( ) error {
dest := net . TCPDestination ( c . config . Address . AsAddress ( ) , net . Port ( c . config . Port ) )
dest . Network = network
rawConn , err := dialer . Dial ( ctx , dest )
if err != nil {
return err
}
conn = rawConn
return nil
} )
if err != nil {
return nil , newError ( "failed to find an available destination" ) . AtWarning ( ) . Base ( err )
}
newError ( "creating udp session to " , network , ":" , c . config . Address ) . WriteToLog ( session . ExportIDToError ( ctx ) )
packetProcessor , err := method . GetUDPClientProcessor ( c . config . Ipsk , c . config . Psk , keyDerivation )
if err != nil {
return nil , newError ( "failed to create UDP client packet processor" ) . Base ( err )
}
return NewClientUDPSession ( ctx , conn , packetProcessor ) , nil
} )
if err != nil {
return nil , newError ( "failed to create UDP session" ) . Base ( err )
}
sessionConn , err := sessionState . NewSessionConn ( )
if err != nil {
return nil , newError ( "failed to create UDP session connection" ) . Base ( err )
}
return sessionConn , nil
}
2023-11-03 16:10:11 -04:00
func NewClient ( ctx context . Context , config * ClientConfig ) ( * Client , error ) {
2023-11-18 18:09:26 -05:00
storage := envctx . EnvironmentFromContext ( ctx ) . ( environment . ProxyEnvironment ) . TransientStorage ( )
udpState , err := NewClientUDPConnState ( )
if err != nil {
return nil , newError ( "failed to create UDP connection state" ) . Base ( err )
}
storage . Put ( ctx , UDPConnectionState , udpState )
2023-11-03 16:10:11 -04:00
return & Client {
config : config ,
ctx : ctx ,
} , nil
}
func init ( ) {
common . Must ( common . RegisterConfig ( ( * ClientConfig ) ( nil ) , func ( ctx context . Context , config interface { } ) ( interface { } , error ) {
clientConfig , ok := config . ( * ClientConfig )
if ! ok {
return nil , newError ( "not a ClientConfig" )
}
return NewClient ( ctx , clientConfig )
} ) )
}