mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-11-26 23:26:03 +03:00
f35ded79ad
UDP and MUX command currently has no flow value. Also the character is the same with or without XTLS
628 lines
19 KiB
Go
628 lines
19 KiB
Go
package inbound
|
|
|
|
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/xtls/xray-core/common"
|
|
"github.com/xtls/xray-core/common/buf"
|
|
"github.com/xtls/xray-core/common/errors"
|
|
"github.com/xtls/xray-core/common/log"
|
|
"github.com/xtls/xray-core/common/net"
|
|
"github.com/xtls/xray-core/common/platform"
|
|
"github.com/xtls/xray-core/common/protocol"
|
|
"github.com/xtls/xray-core/common/retry"
|
|
"github.com/xtls/xray-core/common/session"
|
|
"github.com/xtls/xray-core/common/signal"
|
|
"github.com/xtls/xray-core/common/task"
|
|
core "github.com/xtls/xray-core/core"
|
|
"github.com/xtls/xray-core/features/dns"
|
|
feature_inbound "github.com/xtls/xray-core/features/inbound"
|
|
"github.com/xtls/xray-core/features/policy"
|
|
"github.com/xtls/xray-core/features/routing"
|
|
"github.com/xtls/xray-core/features/stats"
|
|
"github.com/xtls/xray-core/proxy/vless"
|
|
"github.com/xtls/xray-core/proxy/vless/encoding"
|
|
"github.com/xtls/xray-core/transport/internet/stat"
|
|
"github.com/xtls/xray-core/transport/internet/tls"
|
|
"github.com/xtls/xray-core/transport/internet/xtls"
|
|
)
|
|
|
|
var xtls_show = false
|
|
|
|
func init() {
|
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
|
var dc dns.Client
|
|
if err := core.RequireFeatures(ctx, func(d dns.Client) error {
|
|
dc = d
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
return New(ctx, config.(*Config), dc)
|
|
}))
|
|
|
|
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
|
|
|
xtlsShow := platform.NewEnvFlag("xray.vless.xtls.show").GetValue(func() string { return defaultFlagValue })
|
|
if xtlsShow == "true" {
|
|
xtls_show = true
|
|
}
|
|
}
|
|
|
|
// Handler is an inbound connection handler that handles messages in VLess protocol.
|
|
type Handler struct {
|
|
inboundHandlerManager feature_inbound.Manager
|
|
policyManager policy.Manager
|
|
validator *vless.Validator
|
|
dns dns.Client
|
|
fallbacks map[string]map[string]map[string]*Fallback // or nil
|
|
// regexps map[string]*regexp.Regexp // or nil
|
|
}
|
|
|
|
// New creates a new VLess inbound handler.
|
|
func New(ctx context.Context, config *Config, dc dns.Client) (*Handler, error) {
|
|
v := core.MustFromContext(ctx)
|
|
handler := &Handler{
|
|
inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager),
|
|
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
|
validator: new(vless.Validator),
|
|
dns: dc,
|
|
}
|
|
|
|
for _, user := range config.Clients {
|
|
u, err := user.ToMemoryUser()
|
|
if err != nil {
|
|
return nil, newError("failed to get VLESS user").Base(err).AtError()
|
|
}
|
|
if err := handler.AddUser(ctx, u); err != nil {
|
|
return nil, newError("failed to initiate user").Base(err).AtError()
|
|
}
|
|
}
|
|
|
|
if config.Fallbacks != nil {
|
|
handler.fallbacks = make(map[string]map[string]map[string]*Fallback)
|
|
// handler.regexps = make(map[string]*regexp.Regexp)
|
|
for _, fb := range config.Fallbacks {
|
|
if handler.fallbacks[fb.Name] == nil {
|
|
handler.fallbacks[fb.Name] = make(map[string]map[string]*Fallback)
|
|
}
|
|
if handler.fallbacks[fb.Name][fb.Alpn] == nil {
|
|
handler.fallbacks[fb.Name][fb.Alpn] = make(map[string]*Fallback)
|
|
}
|
|
handler.fallbacks[fb.Name][fb.Alpn][fb.Path] = fb
|
|
/*
|
|
if fb.Path != "" {
|
|
if r, err := regexp.Compile(fb.Path); err != nil {
|
|
return nil, newError("invalid path regexp").Base(err).AtError()
|
|
} else {
|
|
handler.regexps[fb.Path] = r
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
if handler.fallbacks[""] != nil {
|
|
for name, apfb := range handler.fallbacks {
|
|
if name != "" {
|
|
for alpn := range handler.fallbacks[""] {
|
|
if apfb[alpn] == nil {
|
|
apfb[alpn] = make(map[string]*Fallback)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for _, apfb := range handler.fallbacks {
|
|
if apfb[""] != nil {
|
|
for alpn, pfb := range apfb {
|
|
if alpn != "" { // && alpn != "h2" {
|
|
for path, fb := range apfb[""] {
|
|
if pfb[path] == nil {
|
|
pfb[path] = fb
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if handler.fallbacks[""] != nil {
|
|
for name, apfb := range handler.fallbacks {
|
|
if name != "" {
|
|
for alpn, pfb := range handler.fallbacks[""] {
|
|
for path, fb := range pfb {
|
|
if apfb[alpn][path] == nil {
|
|
apfb[alpn][path] = fb
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return handler, nil
|
|
}
|
|
|
|
// Close implements common.Closable.Close().
|
|
func (h *Handler) Close() error {
|
|
return errors.Combine(common.Close(h.validator))
|
|
}
|
|
|
|
// AddUser implements proxy.UserManager.AddUser().
|
|
func (h *Handler) AddUser(ctx context.Context, u *protocol.MemoryUser) error {
|
|
return h.validator.Add(u)
|
|
}
|
|
|
|
// RemoveUser implements proxy.UserManager.RemoveUser().
|
|
func (h *Handler) RemoveUser(ctx context.Context, e string) error {
|
|
return h.validator.Del(e)
|
|
}
|
|
|
|
// Network implements proxy.Inbound.Network().
|
|
func (*Handler) Network() []net.Network {
|
|
return []net.Network{net.Network_TCP, net.Network_UNIX}
|
|
}
|
|
|
|
// Process implements proxy.Inbound.Process().
|
|
func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
|
sid := session.ExportIDToError(ctx)
|
|
|
|
iConn := connection
|
|
statConn, ok := iConn.(*stat.CounterConnection)
|
|
if ok {
|
|
iConn = statConn.Connection
|
|
}
|
|
|
|
sessionPolicy := h.policyManager.ForLevel(0)
|
|
if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil {
|
|
return newError("unable to set read deadline").Base(err).AtWarning()
|
|
}
|
|
|
|
first := buf.New()
|
|
defer first.Release()
|
|
|
|
firstLen, _ := first.ReadFrom(connection)
|
|
newError("firstLen = ", firstLen).AtInfo().WriteToLog(sid)
|
|
|
|
reader := &buf.BufferedReader{
|
|
Reader: buf.NewReader(connection),
|
|
Buffer: buf.MultiBuffer{first},
|
|
}
|
|
|
|
var request *protocol.RequestHeader
|
|
var requestAddons *encoding.Addons
|
|
var err error
|
|
|
|
napfb := h.fallbacks
|
|
isfb := napfb != nil
|
|
|
|
if isfb && firstLen < 18 {
|
|
err = newError("fallback directly")
|
|
} else {
|
|
request, requestAddons, isfb, err = encoding.DecodeRequestHeader(isfb, first, reader, h.validator)
|
|
}
|
|
|
|
if err != nil {
|
|
if isfb {
|
|
if err := connection.SetReadDeadline(time.Time{}); err != nil {
|
|
newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid)
|
|
}
|
|
newError("fallback starts").Base(err).AtInfo().WriteToLog(sid)
|
|
|
|
name := ""
|
|
alpn := ""
|
|
if tlsConn, ok := iConn.(*tls.Conn); ok {
|
|
cs := tlsConn.ConnectionState()
|
|
name = cs.ServerName
|
|
alpn = cs.NegotiatedProtocol
|
|
newError("realName = " + name).AtInfo().WriteToLog(sid)
|
|
newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid)
|
|
} else if xtlsConn, ok := iConn.(*xtls.Conn); ok {
|
|
cs := xtlsConn.ConnectionState()
|
|
name = cs.ServerName
|
|
alpn = cs.NegotiatedProtocol
|
|
newError("realName = " + name).AtInfo().WriteToLog(sid)
|
|
newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid)
|
|
}
|
|
name = strings.ToLower(name)
|
|
alpn = strings.ToLower(alpn)
|
|
|
|
if len(napfb) > 1 || napfb[""] == nil {
|
|
if name != "" && napfb[name] == nil {
|
|
match := ""
|
|
for n := range napfb {
|
|
if n != "" && strings.Contains(name, n) && len(n) > len(match) {
|
|
match = n
|
|
}
|
|
}
|
|
name = match
|
|
}
|
|
}
|
|
|
|
if napfb[name] == nil {
|
|
name = ""
|
|
}
|
|
apfb := napfb[name]
|
|
if apfb == nil {
|
|
return newError(`failed to find the default "name" config`).AtWarning()
|
|
}
|
|
|
|
if apfb[alpn] == nil {
|
|
alpn = ""
|
|
}
|
|
pfb := apfb[alpn]
|
|
if pfb == nil {
|
|
return newError(`failed to find the default "alpn" config`).AtWarning()
|
|
}
|
|
|
|
path := ""
|
|
if len(pfb) > 1 || pfb[""] == nil {
|
|
/*
|
|
if lines := bytes.Split(firstBytes, []byte{'\r', '\n'}); len(lines) > 1 {
|
|
if s := bytes.Split(lines[0], []byte{' '}); len(s) == 3 {
|
|
if len(s[0]) < 8 && len(s[1]) > 0 && len(s[2]) == 8 {
|
|
newError("realPath = " + string(s[1])).AtInfo().WriteToLog(sid)
|
|
for _, fb := range pfb {
|
|
if fb.Path != "" && h.regexps[fb.Path].Match(s[1]) {
|
|
path = fb.Path
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
if firstLen >= 18 && first.Byte(4) != '*' { // not h2c
|
|
firstBytes := first.Bytes()
|
|
for i := 4; i <= 8; i++ { // 5 -> 9
|
|
if firstBytes[i] == '/' && firstBytes[i-1] == ' ' {
|
|
search := len(firstBytes)
|
|
if search > 64 {
|
|
search = 64 // up to about 60
|
|
}
|
|
for j := i + 1; j < search; j++ {
|
|
k := firstBytes[j]
|
|
if k == '\r' || k == '\n' { // avoid logging \r or \n
|
|
break
|
|
}
|
|
if k == '?' || k == ' ' {
|
|
path = string(firstBytes[i:j])
|
|
newError("realPath = " + path).AtInfo().WriteToLog(sid)
|
|
if pfb[path] == nil {
|
|
path = ""
|
|
}
|
|
break
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fb := pfb[path]
|
|
if fb == nil {
|
|
return newError(`failed to find the default "path" config`).AtWarning()
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
|
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
|
|
|
|
var conn net.Conn
|
|
if err := retry.ExponentialBackoff(5, 100).On(func() error {
|
|
var dialer net.Dialer
|
|
conn, err = dialer.DialContext(ctx, fb.Type, fb.Dest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return newError("failed to dial to " + fb.Dest).Base(err).AtWarning()
|
|
}
|
|
defer conn.Close()
|
|
|
|
serverReader := buf.NewReader(conn)
|
|
serverWriter := buf.NewWriter(conn)
|
|
|
|
postRequest := func() error {
|
|
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
|
if fb.Xver != 0 {
|
|
ipType := 4
|
|
remoteAddr, remotePort, err := net.SplitHostPort(connection.RemoteAddr().String())
|
|
if err != nil {
|
|
ipType = 0
|
|
}
|
|
localAddr, localPort, err := net.SplitHostPort(connection.LocalAddr().String())
|
|
if err != nil {
|
|
ipType = 0
|
|
}
|
|
if ipType == 4 {
|
|
for i := 0; i < len(remoteAddr); i++ {
|
|
if remoteAddr[i] == ':' {
|
|
ipType = 6
|
|
break
|
|
}
|
|
}
|
|
}
|
|
pro := buf.New()
|
|
defer pro.Release()
|
|
switch fb.Xver {
|
|
case 1:
|
|
if ipType == 0 {
|
|
pro.Write([]byte("PROXY UNKNOWN\r\n"))
|
|
break
|
|
}
|
|
if ipType == 4 {
|
|
pro.Write([]byte("PROXY TCP4 " + remoteAddr + " " + localAddr + " " + remotePort + " " + localPort + "\r\n"))
|
|
} else {
|
|
pro.Write([]byte("PROXY TCP6 " + remoteAddr + " " + localAddr + " " + remotePort + " " + localPort + "\r\n"))
|
|
}
|
|
case 2:
|
|
pro.Write([]byte("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A")) // signature
|
|
if ipType == 0 {
|
|
pro.Write([]byte("\x20\x00\x00\x00")) // v2 + LOCAL + UNSPEC + UNSPEC + 0 bytes
|
|
break
|
|
}
|
|
if ipType == 4 {
|
|
pro.Write([]byte("\x21\x11\x00\x0C")) // v2 + PROXY + AF_INET + STREAM + 12 bytes
|
|
pro.Write(net.ParseIP(remoteAddr).To4())
|
|
pro.Write(net.ParseIP(localAddr).To4())
|
|
} else {
|
|
pro.Write([]byte("\x21\x21\x00\x24")) // v2 + PROXY + AF_INET6 + STREAM + 36 bytes
|
|
pro.Write(net.ParseIP(remoteAddr).To16())
|
|
pro.Write(net.ParseIP(localAddr).To16())
|
|
}
|
|
p1, _ := strconv.ParseUint(remotePort, 10, 16)
|
|
p2, _ := strconv.ParseUint(localPort, 10, 16)
|
|
pro.Write([]byte{byte(p1 >> 8), byte(p1), byte(p2 >> 8), byte(p2)})
|
|
}
|
|
if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pro}); err != nil {
|
|
return newError("failed to set PROXY protocol v", fb.Xver).Base(err).AtWarning()
|
|
}
|
|
}
|
|
if err := buf.Copy(reader, serverWriter, buf.UpdateActivity(timer)); err != nil {
|
|
return newError("failed to fallback request payload").Base(err).AtInfo()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
writer := buf.NewWriter(connection)
|
|
|
|
getResponse := func() error {
|
|
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
|
|
if err := buf.Copy(serverReader, writer, buf.UpdateActivity(timer)); err != nil {
|
|
return newError("failed to deliver response payload").Base(err).AtInfo()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), task.OnSuccess(getResponse, task.Close(writer))); err != nil {
|
|
common.Interrupt(serverReader)
|
|
common.Interrupt(serverWriter)
|
|
return newError("fallback ends").Base(err).AtInfo()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if errors.Cause(err) != io.EOF {
|
|
log.Record(&log.AccessMessage{
|
|
From: connection.RemoteAddr(),
|
|
To: "",
|
|
Status: log.AccessRejected,
|
|
Reason: err,
|
|
})
|
|
err = newError("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo()
|
|
}
|
|
return err
|
|
}
|
|
|
|
if err := connection.SetReadDeadline(time.Time{}); err != nil {
|
|
newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid)
|
|
}
|
|
newError("received request for ", request.Destination()).AtInfo().WriteToLog(sid)
|
|
|
|
inbound := session.InboundFromContext(ctx)
|
|
if inbound == nil {
|
|
panic("no inbound metadata")
|
|
}
|
|
inbound.User = request.User
|
|
|
|
account := request.User.Account.(*vless.MemoryAccount)
|
|
|
|
responseAddons := &encoding.Addons{
|
|
// Flow: requestAddons.Flow,
|
|
}
|
|
|
|
var netConn net.Conn
|
|
var rawConn syscall.RawConn
|
|
allowNoneFlow := false
|
|
accountFlow := account.Flow
|
|
flows := strings.Split(account.Flow, ",")
|
|
for _, f := range flows {
|
|
t := strings.TrimSpace(f)
|
|
if t == "none" {
|
|
allowNoneFlow = true
|
|
} else {
|
|
accountFlow = t
|
|
}
|
|
}
|
|
switch requestAddons.Flow {
|
|
case vless.XRO, vless.XRD, vless.XRV:
|
|
if accountFlow == requestAddons.Flow {
|
|
switch request.Command {
|
|
case protocol.RequestCommandMux:
|
|
return newError(requestAddons.Flow + " doesn't support Mux").AtWarning()
|
|
case protocol.RequestCommandUDP:
|
|
return newError(requestAddons.Flow + " doesn't support UDP").AtWarning()
|
|
case protocol.RequestCommandTCP:
|
|
if requestAddons.Flow == vless.XRV {
|
|
if tlsConn, ok := iConn.(*tls.Conn); ok {
|
|
netConn = tlsConn.NetConn()
|
|
if sc, ok := netConn.(syscall.Conn); ok {
|
|
rawConn, _ = sc.SyscallConn()
|
|
}
|
|
} else if _, ok := iConn.(*tls.UConn); ok {
|
|
return newError("XTLS only supports UTLS fingerprint for the outbound.").AtWarning()
|
|
} else if _, ok := iConn.(*xtls.Conn); ok {
|
|
return newError(`failed to use ` + requestAddons.Flow + `, vision "security" must be "tls"`).AtWarning()
|
|
} else {
|
|
return newError("XTLS only supports TCP, mKCP and DomainSocket for now.").AtWarning()
|
|
}
|
|
} else if xtlsConn, ok := iConn.(*xtls.Conn); ok {
|
|
xtlsConn.RPRX = true
|
|
xtlsConn.SHOW = xtls_show
|
|
xtlsConn.MARK = "XTLS"
|
|
if requestAddons.Flow == vless.XRD {
|
|
xtlsConn.DirectMode = true
|
|
if sc, ok := xtlsConn.NetConn().(syscall.Conn); ok {
|
|
rawConn, _ = sc.SyscallConn()
|
|
}
|
|
}
|
|
} else {
|
|
return newError(`failed to use ` + requestAddons.Flow + `, maybe "security" is not "xtls"`).AtWarning()
|
|
}
|
|
}
|
|
} else {
|
|
return newError(account.ID.String() + " is not able to use " + requestAddons.Flow).AtWarning()
|
|
}
|
|
case "", "none":
|
|
if accountFlow == vless.XRV && !allowNoneFlow && request.Command == protocol.RequestCommandTCP {
|
|
return newError(account.ID.String() + " is not able to use " + vless.XRV +
|
|
". Note the pure tls proxy has certain tls in tls characters. Append \",none\" in flow to suppress").AtWarning()
|
|
}
|
|
default:
|
|
return newError("unknown request flow " + requestAddons.Flow).AtWarning()
|
|
}
|
|
|
|
if request.Command != protocol.RequestCommandMux {
|
|
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
|
From: connection.RemoteAddr(),
|
|
To: request.Destination(),
|
|
Status: log.AccessAccepted,
|
|
Reason: "",
|
|
Email: request.User.Email,
|
|
})
|
|
}
|
|
|
|
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
|
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
|
|
|
|
link, err := dispatcher.Dispatch(ctx, request.Destination())
|
|
if err != nil {
|
|
return newError("failed to dispatch request to ", request.Destination()).Base(err).AtWarning()
|
|
}
|
|
|
|
serverReader := link.Reader // .(*pipe.Reader)
|
|
serverWriter := link.Writer // .(*pipe.Writer)
|
|
enableXtls := false
|
|
isTLS12orAbove := false
|
|
isTLS := false
|
|
var cipher uint16 = 0
|
|
var remainingServerHello int32 = -1
|
|
numberOfPacketToFilter := 8
|
|
|
|
postRequest := func() error {
|
|
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
|
|
|
// default: clientReader := reader
|
|
clientReader := encoding.DecodeBodyAddons(reader, request, requestAddons)
|
|
|
|
var err error
|
|
|
|
if rawConn != nil {
|
|
var counter stats.Counter
|
|
if statConn != nil {
|
|
counter = statConn.ReadCounter
|
|
}
|
|
//TODO enable splice
|
|
ctx = session.ContextWithInbound(ctx, nil)
|
|
if requestAddons.Flow == vless.XRV {
|
|
err = encoding.XtlsRead(clientReader, serverWriter, timer, netConn, rawConn, counter, ctx, account.ID.Bytes(),
|
|
&numberOfPacketToFilter, &enableXtls, &isTLS12orAbove, &isTLS, &cipher, &remainingServerHello)
|
|
} else {
|
|
err = encoding.ReadV(clientReader, serverWriter, timer, iConn.(*xtls.Conn), rawConn, counter, ctx)
|
|
}
|
|
} else {
|
|
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
|
|
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
|
}
|
|
|
|
if err != nil {
|
|
return newError("failed to transfer request payload").Base(err).AtInfo()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
getResponse := func() error {
|
|
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
|
|
|
|
bufferWriter := buf.NewBufferedWriter(buf.NewWriter(connection))
|
|
if err := encoding.EncodeResponseHeader(bufferWriter, request, responseAddons); err != nil {
|
|
return newError("failed to encode response header").Base(err).AtWarning()
|
|
}
|
|
|
|
// default: clientWriter := bufferWriter
|
|
clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, responseAddons)
|
|
userUUID := account.ID.Bytes()
|
|
multiBuffer, err1 := serverReader.ReadMultiBuffer()
|
|
if err1 != nil {
|
|
return err1 // ...
|
|
}
|
|
if requestAddons.Flow == vless.XRV {
|
|
encoding.XtlsFilterTls(multiBuffer, &numberOfPacketToFilter, &enableXtls, &isTLS12orAbove, &isTLS, &cipher, &remainingServerHello, ctx)
|
|
if isTLS {
|
|
multiBuffer = encoding.ReshapeMultiBuffer(ctx, multiBuffer)
|
|
for i, b := range multiBuffer {
|
|
multiBuffer[i] = encoding.XtlsPadding(b, 0x00, &userUUID, ctx)
|
|
}
|
|
}
|
|
}
|
|
if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil {
|
|
return err // ...
|
|
}
|
|
// Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer
|
|
if err := bufferWriter.SetBuffered(false); err != nil {
|
|
return newError("failed to write A response payload").Base(err).AtWarning()
|
|
}
|
|
|
|
var err error
|
|
if rawConn != nil && requestAddons.Flow == vless.XRV {
|
|
var counter stats.Counter
|
|
if statConn != nil {
|
|
counter = statConn.WriteCounter
|
|
}
|
|
err = encoding.XtlsWrite(serverReader, clientWriter, timer, netConn, counter, ctx, &userUUID, &numberOfPacketToFilter,
|
|
&enableXtls, &isTLS12orAbove, &isTLS, &cipher, &remainingServerHello)
|
|
} else {
|
|
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
|
|
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
|
}
|
|
if err != nil {
|
|
return newError("failed to transfer response payload").Base(err).AtInfo()
|
|
}
|
|
// Indicates the end of response payload.
|
|
switch responseAddons.Flow {
|
|
default:
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), getResponse); err != nil {
|
|
common.Interrupt(serverReader)
|
|
common.Interrupt(serverWriter)
|
|
return newError("connection ends").Base(err).AtInfo()
|
|
}
|
|
|
|
return nil
|
|
}
|