// +build !confonly package inbound //go:generate errorgen import ( "context" "io" "strconv" "time" "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/errors" "v2ray.com/core/common/log" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/retry" "v2ray.com/core/common/session" "v2ray.com/core/common/signal" "v2ray.com/core/common/task" "v2ray.com/core/features/dns" feature_inbound "v2ray.com/core/features/inbound" "v2ray.com/core/features/policy" "v2ray.com/core/features/routing" "v2ray.com/core/proxy/vless" "v2ray.com/core/proxy/vless/encoding" "v2ray.com/core/transport/internet" ) 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) })) } // 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 fallback *Fallback // or nil addrport string } // 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.User { 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.Fallback != nil { handler.fallback = config.Fallback handler.addrport = handler.fallback.Addr.AsAddress().String() + ":" + strconv.Itoa(int(handler.fallback.Port)) } 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} } // Process implements proxy.Inbound.Process(). func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher routing.Dispatcher) error { 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() first.ReadFrom(connection) sid := session.ExportIDToError(ctx) newError("firstLen = ", first.Len()).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 var pre *buf.Buffer if h.fallback != nil && first.Len() < 18 { err = newError("fallback directly") pre = buf.New() } else { request, requestAddons, err, pre = encoding.DecodeRequestHeader(reader, h.validator) } if err != nil { if h.fallback != nil && pre != nil { newError("fallback starts").AtInfo().WriteToLog(sid) var conn net.Conn if err := retry.ExponentialBackoff(5, 100).On(func() error { var dialer net.Dialer var err error if h.fallback.Unix != "" { conn, err = dialer.DialContext(ctx, "unix", h.fallback.Unix) } else { conn, err = dialer.DialContext(ctx, "tcp", h.addrport) } if err != nil { return err } return nil }); err != nil { return newError("failed to fallback connection").Base(err).AtWarning() } defer conn.Close() // nolint: errcheck ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) writer := buf.NewWriter(connection) serverReader := buf.NewReader(conn) serverWriter := buf.NewWriter(conn) postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if pre.Len() > 0 { if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pre}); err != nil { return newError("failed to fallback request pre").Base(err).AtWarning() } } if err := buf.Copy(reader, serverWriter, buf.UpdateActivity(timer)); err != nil { return err // ... } return nil } getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) if err := buf.Copy(serverReader, writer, buf.UpdateActivity(timer)); err != nil { return err // ... } 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).AtWarning() } return err } 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, }) } newError("received request for ", request.Destination()).AtInfo().WriteToLog(sid) if err := connection.SetReadDeadline(time.Time{}); err != nil { newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid) } inbound := session.InboundFromContext(ctx) if inbound == nil { panic("no inbound metadata") } inbound.User = request.User 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 serverWriter := link.Writer postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) // default: clientReader := reader clientReader := encoding.DecodeBodyAddons(reader, request, requestAddons) // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer request payload").Base(err).AtInfo() } return nil } getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) responseAddons := &encoding.Addons{ Scheduler: requestAddons.Scheduler, } 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) { multiBuffer, err := serverReader.ReadMultiBuffer() if err != nil { return err // ... } 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() } // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer response payload").Base(err).AtInfo() } // Indicates the end of response payload. switch responseAddons.Scheduler { 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 }