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