github.com/imannamdari/v2ray-core/v5@v5.0.5/proxy/vless/outbound/outbound.go (about)

     1  package outbound
     2  
     3  //go:generate go run github.com/imannamdari/v2ray-core/v5/common/errors/errorgen
     4  
     5  import (
     6  	"context"
     7  
     8  	core "github.com/imannamdari/v2ray-core/v5"
     9  	"github.com/imannamdari/v2ray-core/v5/common"
    10  	"github.com/imannamdari/v2ray-core/v5/common/buf"
    11  	"github.com/imannamdari/v2ray-core/v5/common/net"
    12  	"github.com/imannamdari/v2ray-core/v5/common/protocol"
    13  	"github.com/imannamdari/v2ray-core/v5/common/retry"
    14  	"github.com/imannamdari/v2ray-core/v5/common/serial"
    15  	"github.com/imannamdari/v2ray-core/v5/common/session"
    16  	"github.com/imannamdari/v2ray-core/v5/common/signal"
    17  	"github.com/imannamdari/v2ray-core/v5/common/task"
    18  	"github.com/imannamdari/v2ray-core/v5/features/policy"
    19  	"github.com/imannamdari/v2ray-core/v5/proxy"
    20  	"github.com/imannamdari/v2ray-core/v5/proxy/vless"
    21  	"github.com/imannamdari/v2ray-core/v5/proxy/vless/encoding"
    22  	"github.com/imannamdari/v2ray-core/v5/transport"
    23  	"github.com/imannamdari/v2ray-core/v5/transport/internet"
    24  )
    25  
    26  func init() {
    27  	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
    28  		return New(ctx, config.(*Config))
    29  	}))
    30  
    31  	common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
    32  		simplifiedClient := config.(*SimplifiedConfig)
    33  		fullClient := &Config{Vnext: []*protocol.ServerEndpoint{
    34  			{
    35  				Address: simplifiedClient.Address,
    36  				Port:    simplifiedClient.Port,
    37  				User: []*protocol.User{
    38  					{
    39  						Account: serial.ToTypedMessage(&vless.Account{Id: simplifiedClient.Uuid, Encryption: "none"}),
    40  					},
    41  				},
    42  			},
    43  		}}
    44  
    45  		return common.CreateObject(ctx, fullClient)
    46  	}))
    47  }
    48  
    49  // Handler is an outbound connection handler for VLess protocol.
    50  type Handler struct {
    51  	serverList    *protocol.ServerList
    52  	serverPicker  protocol.ServerPicker
    53  	policyManager policy.Manager
    54  }
    55  
    56  // New creates a new VLess outbound handler.
    57  func New(ctx context.Context, config *Config) (*Handler, error) {
    58  	serverList := protocol.NewServerList()
    59  	for _, rec := range config.Vnext {
    60  		s, err := protocol.NewServerSpecFromPB(rec)
    61  		if err != nil {
    62  			return nil, newError("failed to parse server spec").Base(err).AtError()
    63  		}
    64  		serverList.AddServer(s)
    65  	}
    66  
    67  	v := core.MustFromContext(ctx)
    68  	handler := &Handler{
    69  		serverList:    serverList,
    70  		serverPicker:  protocol.NewRoundRobinServerPicker(serverList),
    71  		policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
    72  	}
    73  
    74  	return handler, nil
    75  }
    76  
    77  // Process implements proxy.Outbound.Process().
    78  func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
    79  	var rec *protocol.ServerSpec
    80  	var conn internet.Connection
    81  
    82  	if err := retry.ExponentialBackoff(5, 200).On(func() error {
    83  		rec = h.serverPicker.PickServer()
    84  		var err error
    85  		conn, err = dialer.Dial(ctx, rec.Destination())
    86  		if err != nil {
    87  			return err
    88  		}
    89  		return nil
    90  	}); err != nil {
    91  		return newError("failed to find an available destination").Base(err).AtWarning()
    92  	}
    93  	defer conn.Close()
    94  
    95  	outbound := session.OutboundFromContext(ctx)
    96  	if outbound == nil || !outbound.Target.IsValid() {
    97  		return newError("target not specified").AtError()
    98  	}
    99  
   100  	target := outbound.Target
   101  	newError("tunneling request to ", target, " via ", rec.Destination().NetAddr()).AtInfo().WriteToLog(session.ExportIDToError(ctx))
   102  
   103  	command := protocol.RequestCommandTCP
   104  	if target.Network == net.Network_UDP {
   105  		command = protocol.RequestCommandUDP
   106  	}
   107  	if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" {
   108  		command = protocol.RequestCommandMux
   109  	}
   110  
   111  	request := &protocol.RequestHeader{
   112  		Version: encoding.Version,
   113  		User:    rec.PickUser(),
   114  		Command: command,
   115  		Address: target.Address,
   116  		Port:    target.Port,
   117  	}
   118  
   119  	account := request.User.Account.(*vless.MemoryAccount)
   120  
   121  	requestAddons := &encoding.Addons{
   122  		Flow: account.Flow,
   123  	}
   124  
   125  	sessionPolicy := h.policyManager.ForLevel(request.User.Level)
   126  	ctx, cancel := context.WithCancel(ctx)
   127  	timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
   128  
   129  	clientReader := link.Reader // .(*pipe.Reader)
   130  	clientWriter := link.Writer // .(*pipe.Writer)
   131  
   132  	postRequest := func() error {
   133  		defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
   134  
   135  		bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
   136  		if err := encoding.EncodeRequestHeader(bufferWriter, request, requestAddons); err != nil {
   137  			return newError("failed to encode request header").Base(err).AtWarning()
   138  		}
   139  
   140  		// default: serverWriter := bufferWriter
   141  		serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons)
   142  		if err := buf.CopyOnceTimeout(clientReader, serverWriter, proxy.FirstPayloadTimeout); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
   143  			return err // ...
   144  		}
   145  
   146  		// Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
   147  		if err := bufferWriter.SetBuffered(false); err != nil {
   148  			return newError("failed to write A request payload").Base(err).AtWarning()
   149  		}
   150  
   151  		// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
   152  		if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil {
   153  			return newError("failed to transfer request payload").Base(err).AtInfo()
   154  		}
   155  
   156  		return nil
   157  	}
   158  
   159  	getResponse := func() error {
   160  		defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
   161  
   162  		responseAddons, err := encoding.DecodeResponseHeader(conn, request)
   163  		if err != nil {
   164  			return newError("failed to decode response header").Base(err).AtInfo()
   165  		}
   166  
   167  		// default: serverReader := buf.NewReader(conn)
   168  		serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons)
   169  
   170  		// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
   171  		if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil {
   172  			return newError("failed to transfer response payload").Base(err).AtInfo()
   173  		}
   174  
   175  		return nil
   176  	}
   177  
   178  	if err := task.Run(ctx, postRequest, task.OnSuccess(getResponse, task.Close(clientWriter))); err != nil {
   179  		return newError("connection ends").Base(err).AtInfo()
   180  	}
   181  
   182  	return nil
   183  }