github.com/xmplusdev/xray-core@v1.8.10/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/xmplusdev/xray-core/common" 15 "github.com/xmplusdev/xray-core/common/buf" 16 "github.com/xmplusdev/xray-core/common/net" 17 "github.com/xmplusdev/xray-core/common/session" 18 "github.com/xmplusdev/xray-core/common/singbridge" 19 "github.com/xmplusdev/xray-core/transport" 20 "github.com/xmplusdev/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 inbound.SetCanSpliceCopy(3) 69 } 70 71 outbound := session.OutboundFromContext(ctx) 72 if outbound == nil || !outbound.Target.IsValid() { 73 return newError("target not specified") 74 } 75 outbound.Name = "shadowsocks-2022" 76 destination := outbound.Target 77 network := destination.Network 78 79 newError("tunneling request to ", destination, " via ", o.server.NetAddr()).WriteToLog(session.ExportIDToError(ctx)) 80 81 serverDestination := o.server 82 if o.uotClient != nil { 83 serverDestination.Network = net.Network_TCP 84 } else { 85 serverDestination.Network = network 86 } 87 connection, err := dialer.Dial(ctx, serverDestination) 88 if err != nil { 89 return newError("failed to connect to server").Base(err) 90 } 91 92 if session.TimeoutOnlyFromContext(ctx) { 93 ctx, _ = context.WithCancel(context.Background()) 94 } 95 96 if network == net.Network_TCP { 97 serverConn := o.method.DialEarlyConn(connection, singbridge.ToSocksaddr(destination)) 98 var handshake bool 99 if timeoutReader, isTimeoutReader := link.Reader.(buf.TimeoutReader); isTimeoutReader { 100 mb, err := timeoutReader.ReadMultiBufferTimeout(time.Millisecond * 100) 101 if err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { 102 return newError("read payload").Base(err) 103 } 104 payload := B.New() 105 for { 106 payload.Reset() 107 nb, n := buf.SplitBytes(mb, payload.FreeBytes()) 108 if n > 0 { 109 payload.Truncate(n) 110 _, err = serverConn.Write(payload.Bytes()) 111 if err != nil { 112 payload.Release() 113 return newError("write payload").Base(err) 114 } 115 handshake = true 116 } 117 if nb.IsEmpty() { 118 break 119 } 120 mb = nb 121 } 122 payload.Release() 123 } 124 if !handshake { 125 _, err = serverConn.Write(nil) 126 if err != nil { 127 return newError("client handshake").Base(err) 128 } 129 } 130 return singbridge.CopyConn(ctx, inboundConn, link, serverConn) 131 } else { 132 var packetConn N.PacketConn 133 if pc, isPacketConn := inboundConn.(N.PacketConn); isPacketConn { 134 packetConn = pc 135 } else if nc, isNetPacket := inboundConn.(net.PacketConn); isNetPacket { 136 packetConn = bufio.NewPacketConn(nc) 137 } else { 138 packetConn = &singbridge.PacketConnWrapper{ 139 Reader: link.Reader, 140 Writer: link.Writer, 141 Conn: inboundConn, 142 Dest: destination, 143 } 144 } 145 146 if o.uotClient != nil { 147 uConn, err := o.uotClient.DialEarlyConn(o.method.DialEarlyConn(connection, uot.RequestDestination(o.uotClient.Version)), false, singbridge.ToSocksaddr(destination)) 148 if err != nil { 149 return err 150 } 151 return singbridge.ReturnError(bufio.CopyPacketConn(ctx, packetConn, uConn)) 152 } else { 153 serverConn := o.method.DialPacketConn(connection) 154 return singbridge.ReturnError(bufio.CopyPacketConn(ctx, packetConn, serverConn)) 155 } 156 } 157 }