From 5a3c7fdd208fb54aecf2bd210e3f857689bd3ac7 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 6 Jan 2018 00:32:21 +0100 Subject: [PATCH 01/63] remove use of unsafe --- common/log/log.go | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/common/log/log.go b/common/log/log.go index fda2431cc..c7cdfadf8 100644 --- a/common/log/log.go +++ b/common/log/log.go @@ -1,8 +1,7 @@ package log import ( - "sync/atomic" - "unsafe" + "sync" "v2ray.com/core/common/serial" ) @@ -30,12 +29,11 @@ func (m *GeneralMessage) String() string { // Record writes a message into log stream. func Record(msg Message) { - h := (*Handler)(atomic.LoadPointer(&logHandler)) - (*h).Handle(msg) + logHandler.Handle(msg) } var ( - logHandler unsafe.Pointer + logHandler syncHandler ) // RegisterHandler register a new handler as current log handler. Previous registered handler will be discarded. @@ -43,5 +41,26 @@ func RegisterHandler(handler Handler) { if handler == nil { panic("Log handler is nil") } - atomic.StorePointer(&logHandler, unsafe.Pointer(&handler)) + logHandler.Set(handler) +} + +type syncHandler struct { + sync.RWMutex + Handler +} + +func (h *syncHandler) Handle(msg Message) { + h.RLock() + defer h.RUnlock() + + if h.Handler != nil { + h.Handler.Handle(msg) + } +} + +func (h *syncHandler) Set(handler Handler) { + h.Lock() + defer h.Unlock() + + h.Handler = handler } From 292d7cc353dd02affe641eb5547cf0e95f53712f Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 10 Jan 2018 12:22:37 +0100 Subject: [PATCH 02/63] massive refactoring for interoperability --- app/app.go | 2 - app/dispatcher/{impl => }/default.go | 53 +++--- app/dispatcher/dispatcher.go | 20 +-- app/dispatcher/errors.generated.go | 5 + app/dispatcher/impl/errors.generated.go | 7 - app/dispatcher/{impl => }/sniffer.go | 2 +- app/dispatcher/{impl => }/sniffer_test.go | 5 +- app/dns/nameserver.go | 4 +- app/dns/server.go | 58 +++---- app/dns/server_test.go | 60 +++---- app/policy/config.go | 16 ++ app/{ => policy}/errors.generated.go | 4 +- app/policy/manager.go | 61 +++++++ app/policy/manager/manager.go | 68 -------- app/policy/policy.go | 19 +-- app/proxyman/config.go | 17 -- app/proxyman/config.pb.go | 187 ++++++-------------- app/proxyman/config.proto | 23 --- app/proxyman/errors.generated.go | 4 +- app/proxyman/inbound/always.go | 8 +- app/proxyman/inbound/dynamic.go | 6 +- app/proxyman/inbound/inbound.go | 85 +++++----- app/proxyman/inbound/worker.go | 6 +- app/proxyman/mux/mux.go | 27 ++- app/proxyman/outbound/handler.go | 38 +++-- app/proxyman/outbound/handler_test.go | 15 ++ app/proxyman/outbound/outbound.go | 42 ++--- app/proxyman/proxyman.go | 44 ----- app/router/router.go | 61 +++---- app/router/router_test.go | 39 +++-- app/space.go | 132 --------------- common/interfaces.go | 10 ++ common/protocol/user.pb.go | 2 +- common/router/dispatcher.go | 12 ++ common/router/router.go | 53 ++++++ config.pb.go | 152 ++++++++++++++--- config.proto | 27 ++- context.go | 17 ++ dns.go | 57 +++++++ main/distro/all/all.go | 4 +- network.go | 165 ++++++++++++++++++ policy.go | 117 +++++++++++++ proxy/dokodemo/dokodemo.go | 61 +++---- proxy/freedom/freedom.go | 58 +++---- proxy/http/server.go | 49 +++--- proxy/proxy.go | 4 +- proxy/shadowsocks/client.go | 29 ++-- proxy/shadowsocks/server.go | 44 ++--- proxy/socks/server.go | 50 +++--- proxy/vmess/inbound/inbound.go | 55 +++--- proxy/vmess/outbound/outbound.go | 34 ++-- router.go | 118 +++++++++++++ testing/scenarios/common.go | 11 ++ testing/scenarios/dns_test.go | 4 +- testing/scenarios/dokodemo_test.go | 16 +- testing/scenarios/feature_test.go | 44 ++--- testing/scenarios/http_test.go | 16 +- testing/scenarios/shadowsocks_test.go | 52 +++--- testing/scenarios/socks_test.go | 20 +-- testing/scenarios/tls_test.go | 24 +-- testing/scenarios/transport_test.go | 8 +- testing/scenarios/vmess_test.go | 72 ++++---- transport/internet/config.pb.go | 5 +- transport/internet/udp/dispatcher.go | 6 +- v2ray.go | 197 ++++++++++++---------- v2ray_test.go | 4 +- 66 files changed, 1515 insertions(+), 1200 deletions(-) rename app/dispatcher/{impl => }/default.go (70%) create mode 100644 app/dispatcher/errors.generated.go delete mode 100644 app/dispatcher/impl/errors.generated.go rename app/dispatcher/{impl => }/sniffer.go (99%) rename app/dispatcher/{impl => }/sniffer_test.go (99%) rename app/{ => policy}/errors.generated.go (64%) create mode 100644 app/policy/manager.go delete mode 100644 app/policy/manager/manager.go create mode 100644 app/proxyman/outbound/handler_test.go delete mode 100644 app/space.go create mode 100644 common/interfaces.go create mode 100644 common/router/dispatcher.go create mode 100644 common/router/router.go create mode 100644 context.go create mode 100644 dns.go create mode 100644 network.go create mode 100644 policy.go create mode 100644 router.go diff --git a/app/app.go b/app/app.go index c688df1e7..4879f7a48 100644 --- a/app/app.go +++ b/app/app.go @@ -1,3 +1 @@ package app - -//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg app -path App diff --git a/app/dispatcher/impl/default.go b/app/dispatcher/default.go similarity index 70% rename from app/dispatcher/impl/default.go rename to app/dispatcher/default.go index 22cc0f59e..5f103a1f1 100644 --- a/app/dispatcher/impl/default.go +++ b/app/dispatcher/default.go @@ -1,4 +1,4 @@ -package impl +package dispatcher //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg impl -path App,Dispatcher,Default @@ -6,10 +6,8 @@ import ( "context" "time" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" + "v2ray.com/core" "v2ray.com/core/app/proxyman" - "v2ray.com/core/app/router" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" @@ -21,31 +19,27 @@ var ( errSniffingTimeout = newError("timeout on sniffing") ) -var ( - _ app.Application = (*DefaultDispatcher)(nil) -) - // DefaultDispatcher is a default implementation of Dispatcher. type DefaultDispatcher struct { - ohm proxyman.OutboundHandlerManager - router *router.Router + ohm core.OutboundHandlerManager + router core.Router } // NewDefaultDispatcher create a new DefaultDispatcher. -func NewDefaultDispatcher(ctx context.Context, config *dispatcher.Config) (*DefaultDispatcher, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context") +func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatcher, error) { + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context.") + } + + d := &DefaultDispatcher{ + ohm: v.OutboundHandlerManager(), + router: v.Router(), + } + + if err := v.RegisterFeature((*core.Dispatcher)(nil), d); err != nil { + return nil, newError("unable to register Dispatcher") } - d := &DefaultDispatcher{} - space.On(app.SpaceInitializing, func(interface{}) error { - d.ohm = proxyman.OutboundHandlerManagerFromSpace(space) - if d.ohm == nil { - return newError("OutboundHandlerManager is not found in the space") - } - d.router = router.FromSpace(space) - return nil - }) return d, nil } @@ -57,12 +51,7 @@ func (*DefaultDispatcher) Start() error { // Close implements app.Application. func (*DefaultDispatcher) Close() {} -// Interface implements app.Application. -func (*DefaultDispatcher) Interface() interface{} { - return (*dispatcher.Interface)(nil) -} - -// Dispatch implements Dispatcher.Interface. +// Dispatch implements core.Dispatcher. func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) { if !destination.IsValid() { panic("Dispatcher: Invalid destination.") @@ -120,7 +109,7 @@ func snifer(ctx context.Context, sniferList []proxyman.KnownProtocols, outbound func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.OutboundRay, destination net.Destination) { dispatcher := d.ohm.GetDefaultHandler() if d.router != nil { - if tag, err := d.router.TakeDetour(ctx); err == nil { + if tag, err := d.router.PickRoute(ctx); err == nil { if handler := d.ohm.GetHandler(tag); handler != nil { newError("taking detour [", tag, "] for [", destination, "]").WriteToLog() dispatcher = handler @@ -135,7 +124,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.Out } func init() { - common.Must(common.RegisterConfig((*dispatcher.Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { - return NewDefaultDispatcher(ctx, config.(*dispatcher.Config)) + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return NewDefaultDispatcher(ctx, config.(*Config)) })) } diff --git a/app/dispatcher/dispatcher.go b/app/dispatcher/dispatcher.go index 67b6486f1..83888b94f 100644 --- a/app/dispatcher/dispatcher.go +++ b/app/dispatcher/dispatcher.go @@ -1,21 +1,3 @@ package dispatcher -import ( - "context" - - "v2ray.com/core/app" - "v2ray.com/core/common/net" - "v2ray.com/core/transport/ray" -) - -// Interface dispatch a packet and possibly further network payload to its destination. -type Interface interface { - Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error) -} - -func FromSpace(space app.Space) Interface { - if app := space.GetApplication((*Interface)(nil)); app != nil { - return app.(Interface) - } - return nil -} +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg dispatcher -path App,Dispatcher diff --git a/app/dispatcher/errors.generated.go b/app/dispatcher/errors.generated.go new file mode 100644 index 000000000..204f6d572 --- /dev/null +++ b/app/dispatcher/errors.generated.go @@ -0,0 +1,5 @@ +package dispatcher + +import "v2ray.com/core/common/errors" + +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Dispatcher") } diff --git a/app/dispatcher/impl/errors.generated.go b/app/dispatcher/impl/errors.generated.go deleted file mode 100644 index 885ba26d0..000000000 --- a/app/dispatcher/impl/errors.generated.go +++ /dev/null @@ -1,7 +0,0 @@ -package impl - -import "v2ray.com/core/common/errors" - -func newError(values ...interface{}) *errors.Error { - return errors.New(values...).Path("App", "Dispatcher", "Default") -} diff --git a/app/dispatcher/impl/sniffer.go b/app/dispatcher/sniffer.go similarity index 99% rename from app/dispatcher/impl/sniffer.go rename to app/dispatcher/sniffer.go index ba4add252..a02d81054 100644 --- a/app/dispatcher/impl/sniffer.go +++ b/app/dispatcher/sniffer.go @@ -1,4 +1,4 @@ -package impl +package dispatcher import ( "bytes" diff --git a/app/dispatcher/impl/sniffer_test.go b/app/dispatcher/sniffer_test.go similarity index 99% rename from app/dispatcher/impl/sniffer_test.go rename to app/dispatcher/sniffer_test.go index 9ae7eac8b..1ac11f1b9 100644 --- a/app/dispatcher/impl/sniffer_test.go +++ b/app/dispatcher/sniffer_test.go @@ -1,11 +1,10 @@ -package impl_test +package dispatcher_test import ( "testing" + . "v2ray.com/core/app/dispatcher" "v2ray.com/core/app/proxyman" - - . "v2ray.com/core/app/dispatcher/impl" . "v2ray.com/ext/assert" ) diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index 785739ea1..aef28064f 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -6,7 +6,7 @@ import ( "time" "github.com/miekg/dns" - "v2ray.com/core/app/dispatcher" + "v2ray.com/core" "v2ray.com/core/common/buf" "v2ray.com/core/common/dice" "v2ray.com/core/common/net" @@ -48,7 +48,7 @@ type UDPNameServer struct { nextCleanup time.Time } -func NewUDPNameServer(address net.Destination, dispatcher dispatcher.Interface) *UDPNameServer { +func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPNameServer { s := &UDPNameServer{ address: address, requests: make(map[uint16]*PendingRequest), diff --git a/app/dns/server.go b/app/dns/server.go index d8494e455..c26c7985c 100644 --- a/app/dns/server.go +++ b/app/dns/server.go @@ -1,6 +1,6 @@ package dns -//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg server -path App,DNS,Server +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg dns -path App,DNS import ( "context" @@ -8,8 +8,7 @@ import ( "time" dnsmsg "github.com/miekg/dns" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/net" ) @@ -41,39 +40,38 @@ type Server struct { } func New(ctx context.Context, config *Config) (*Server, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context") - } server := &Server{ records: make(map[string]*DomainRecord), servers: make([]NameServer, len(config.NameServers)), hosts: config.GetInternalHosts(), } - space.On(app.SpaceInitializing, func(interface{}) error { - disp := dispatcher.FromSpace(space) - if disp == nil { - return newError("dispatcher is not found in the space") - } - for idx, destPB := range config.NameServers { - address := destPB.Address.AsAddress() - if address.Family().IsDomain() && address.Domain() == "localhost" { - server.servers[idx] = &LocalNameServer{} - } else { - dest := destPB.AsDestination() - if dest.Network == net.Network_Unknown { - dest.Network = net.Network_UDP - } - if dest.Network == net.Network_UDP { - server.servers[idx] = NewUDPNameServer(dest, disp) - } + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context.") + } + + if err := v.RegisterFeature((*core.DNSClient)(nil), server); err != nil { + return nil, newError("unable to register DNSClient.").Base(err) + } + + for idx, destPB := range config.NameServers { + address := destPB.Address.AsAddress() + if address.Family().IsDomain() && address.Domain() == "localhost" { + server.servers[idx] = &LocalNameServer{} + } else { + dest := destPB.AsDestination() + if dest.Network == net.Network_Unknown { + dest.Network = net.Network_UDP + } + if dest.Network == net.Network_UDP { + server.servers[idx] = NewUDPNameServer(dest, v.Dispatcher()) } } - if len(config.NameServers) == 0 { - server.servers = append(server.servers, &LocalNameServer{}) - } - return nil - }) + } + if len(config.NameServers) == 0 { + server.servers = append(server.servers, &LocalNameServer{}) + } + return server, nil } @@ -82,12 +80,10 @@ func (*Server) Interface() interface{} { } func (s *Server) Start() error { - net.RegisterIPResolver(s) return nil } func (*Server) Close() { - net.RegisterIPResolver(net.SystemIPResolver()) } func (s *Server) GetCached(domain string) []net.IP { diff --git a/app/dns/server_test.go b/app/dns/server_test.go index d773f8271..5b0a21805 100644 --- a/app/dns/server_test.go +++ b/app/dns/server_test.go @@ -1,18 +1,14 @@ package dns_test import ( - "context" "testing" - "v2ray.com/core/app" + "v2ray.com/core" "v2ray.com/core/app/dispatcher" - _ "v2ray.com/core/app/dispatcher/impl" . "v2ray.com/core/app/dns" - "v2ray.com/core/app/policy" - _ "v2ray.com/core/app/policy/manager" "v2ray.com/core/app/proxyman" + "v2ray.com/core/app/policy" _ "v2ray.com/core/app/proxyman/outbound" - "v2ray.com/core/common" "v2ray.com/core/common/net" "v2ray.com/core/common/serial" "v2ray.com/core/proxy/freedom" @@ -54,50 +50,50 @@ func TestUDPServer(t *testing.T) { go dnsServer.ListenAndServe() - config := &Config{ - NameServers: []*net.Endpoint{ - { - Network: net.Network_UDP, - Address: &net.IPOrDomain{ - Address: &net.IPOrDomain_Ip{ - Ip: []byte{127, 0, 0, 1}, + config := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&Config{ + NameServers: []*net.Endpoint{ + { + Network: net.Network_UDP, + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, + }, + }, + Port: uint32(port), }, }, - Port: uint32(port), + }), + serial.ToTypedMessage(&dispatcher.Config{}), + serial.ToTypedMessage(&proxyman.OutboundConfig{}), + serial.ToTypedMessage(&policy.Config{}), + }, + Outbound: []*core.OutboundHandlerConfig{ + &core.OutboundHandlerConfig{ + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } - ctx := context.Background() - space := app.NewSpace() + v, err := core.New(config) + assert(err, IsNil) - ctx = app.ContextWithSpace(ctx, space) - common.Must(app.AddApplicationToSpace(ctx, config)) - common.Must(app.AddApplicationToSpace(ctx, &dispatcher.Config{})) - common.Must(app.AddApplicationToSpace(ctx, &proxyman.OutboundConfig{})) - common.Must(app.AddApplicationToSpace(ctx, &policy.Config{})) + client := v.DNSClient() - om := proxyman.OutboundHandlerManagerFromSpace(space) - om.AddHandler(ctx, &proxyman.OutboundHandlerConfig{ - ProxySettings: serial.ToTypedMessage(&freedom.Config{}), - }) - - common.Must(space.Initialize()) - common.Must(space.Start()) - - ips, err := net.LookupIP("google.com") + ips, err := client.LookupIP("google.com") assert(err, IsNil) assert(len(ips), Equals, 1) assert([]byte(ips[0]), Equals, []byte{8, 8, 8, 8}) - ips, err = net.LookupIP("facebook.com") + ips, err = client.LookupIP("facebook.com") assert(err, IsNil) assert(len(ips), Equals, 1) assert([]byte(ips[0]), Equals, []byte{9, 9, 9, 9}) dnsServer.Shutdown() - ips, err = net.LookupIP("google.com") + ips, err = client.LookupIP("google.com") assert(err, IsNil) assert(len(ips), Equals, 1) assert([]byte(ips[0]), Equals, []byte{8, 8, 8, 8}) diff --git a/app/policy/config.go b/app/policy/config.go index 350d0751b..4d2f9452d 100644 --- a/app/policy/config.go +++ b/app/policy/config.go @@ -2,10 +2,15 @@ package policy import ( "time" + + "v2ray.com/core" ) // Duration converts Second to time.Duration. func (s *Second) Duration() time.Duration { + if s == nil { + return 0 + } return time.Second * time.Duration(s.Value) } @@ -26,3 +31,14 @@ func (p *Policy) OverrideWith(another *Policy) { } } } + +func (p *Policy) ToCorePolicy() core.Policy { + var cp core.Policy + if p.Timeout != nil { + cp.Timeouts.ConnectionIdle = p.Timeout.ConnectionIdle.Duration() + cp.Timeouts.Handshake = p.Timeout.Handshake.Duration() + cp.Timeouts.DownlinkOnly = p.Timeout.DownlinkOnly.Duration() + cp.Timeouts.UplinkOnly = p.Timeout.UplinkOnly.Duration() + } + return cp +} diff --git a/app/errors.generated.go b/app/policy/errors.generated.go similarity index 64% rename from app/errors.generated.go rename to app/policy/errors.generated.go index 3931523b9..3798ce91f 100644 --- a/app/errors.generated.go +++ b/app/policy/errors.generated.go @@ -1,5 +1,5 @@ -package app +package policy import "v2ray.com/core/common/errors" -func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App") } +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Policy") } diff --git a/app/policy/manager.go b/app/policy/manager.go new file mode 100644 index 000000000..d21eec4f2 --- /dev/null +++ b/app/policy/manager.go @@ -0,0 +1,61 @@ +package policy + +import ( + "context" + + "v2ray.com/core" + "v2ray.com/core/common" +) + +// Instance is an instance of Policy manager. +type Instance struct { + levels map[uint32]core.Policy +} + +// New creates new Policy manager instance. +func New(ctx context.Context, config *Config) (*Instance, error) { + m := &Instance{ + levels: make(map[uint32]core.Policy), + } + if len(config.Level) > 0 { + for lv, p := range config.Level { + dp := core.DefaultPolicy() + dp.OverrideWith(p.ToCorePolicy()) + m.levels[lv] = dp + } + } + + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context.") + } + + if err := v.RegisterFeature((*core.PolicyManager)(nil), m); err != nil { + return nil, newError("unable to register PolicyManager in core").Base(err).AtError() + } + + return m, nil +} + +// ForLevel implements core.PolicyManager. +func (m *Instance) ForLevel(level uint32) core.Policy { + if p, ok := m.levels[level]; ok { + return p + } + return core.DefaultPolicy() +} + +// Start implements app.Application.Start(). +func (m *Instance) Start() error { + return nil +} + +// Close implements app.Application.Close(). +func (m *Instance) Close() { +} + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return New(ctx, config.(*Config)) + })) +} diff --git a/app/policy/manager/manager.go b/app/policy/manager/manager.go deleted file mode 100644 index e84c462b0..000000000 --- a/app/policy/manager/manager.go +++ /dev/null @@ -1,68 +0,0 @@ -package manager - -import ( - "context" - - "v2ray.com/core/app/policy" - "v2ray.com/core/common" -) - -// Instance is an instance of Policy manager. -type Instance struct { - levels map[uint32]*policy.Policy -} - -// New creates new Policy manager instance. -func New(ctx context.Context, config *policy.Config) (*Instance, error) { - levels := config.Level - if levels == nil { - levels = make(map[uint32]*policy.Policy) - } - for _, p := range levels { - g := global() - g.OverrideWith(p) - *p = g - } - return &Instance{ - levels: levels, - }, nil -} - -func global() policy.Policy { - return policy.Policy{ - Timeout: &policy.Policy_Timeout{ - Handshake: &policy.Second{Value: 4}, - ConnectionIdle: &policy.Second{Value: 300}, - UplinkOnly: &policy.Second{Value: 5}, - DownlinkOnly: &policy.Second{Value: 30}, - }, - } -} - -// GetPolicy implements policy.Manager. -func (m *Instance) GetPolicy(level uint32) policy.Policy { - if p, ok := m.levels[level]; ok { - return *p - } - return global() -} - -// Start implements app.Application.Start(). -func (m *Instance) Start() error { - return nil -} - -// Close implements app.Application.Close(). -func (m *Instance) Close() { -} - -// Interface implement app.Application.Interface(). -func (m *Instance) Interface() interface{} { - return (*policy.Manager)(nil) -} - -func init() { - common.Must(common.RegisterConfig((*policy.Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { - return New(ctx, config.(*policy.Config)) - })) -} diff --git a/app/policy/policy.go b/app/policy/policy.go index f67e736e9..32554afee 100644 --- a/app/policy/policy.go +++ b/app/policy/policy.go @@ -1,20 +1,3 @@ package policy -import ( - "v2ray.com/core/app" -) - -// Manager is an utility to manage policy per user level. -type Manager interface { - // GetPolicy returns the Policy for the given user level. - GetPolicy(level uint32) Policy -} - -// FromSpace returns the policy.Manager in a space. -func FromSpace(space app.Space) Manager { - app := space.GetApplication((*Manager)(nil)) - if app == nil { - return nil - } - return app.(Manager) -} +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg policy -path App,Policy diff --git a/app/proxyman/config.go b/app/proxyman/config.go index 9eae4e3fa..6f20ba489 100644 --- a/app/proxyman/config.go +++ b/app/proxyman/config.go @@ -1,11 +1,5 @@ package proxyman -import ( - "context" - - "v2ray.com/core/proxy" -) - func (s *AllocationStrategy) GetConcurrencyValue() uint32 { if s == nil || s.Concurrency == nil { return 3 @@ -19,14 +13,3 @@ func (s *AllocationStrategy) GetRefreshValue() uint32 { } return s.Refresh.Value } - -func (c *OutboundHandlerConfig) GetProxyHandler(ctx context.Context) (proxy.Outbound, error) { - if c == nil { - return nil, newError("OutboundHandlerConfig is nil") - } - config, err := c.ProxySettings.GetInstance() - if err != nil { - return nil, err - } - return proxy.CreateOutboundHandler(ctx, config) -} diff --git a/app/proxyman/config.pb.go b/app/proxyman/config.pb.go index 51df28603..3a64376e0 100644 --- a/app/proxyman/config.pb.go +++ b/app/proxyman/config.pb.go @@ -3,7 +3,6 @@ package proxyman import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" -import v2ray_core_common_serial "v2ray.com/core/common/serial" import v2ray_core_common_net "v2ray.com/core/common/net" import v2ray_core_common_net1 "v2ray.com/core/common/net" import v2ray_core_transport_internet "v2ray.com/core/transport/internet" @@ -211,45 +210,13 @@ func (m *ReceiverConfig) GetDomainOverride() []KnownProtocols { return nil } -type InboundHandlerConfig struct { - Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` - ReceiverSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings" json:"receiver_settings,omitempty"` - ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"` -} - -func (m *InboundHandlerConfig) Reset() { *m = InboundHandlerConfig{} } -func (m *InboundHandlerConfig) String() string { return proto.CompactTextString(m) } -func (*InboundHandlerConfig) ProtoMessage() {} -func (*InboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } - -func (m *InboundHandlerConfig) GetTag() string { - if m != nil { - return m.Tag - } - return "" -} - -func (m *InboundHandlerConfig) GetReceiverSettings() *v2ray_core_common_serial.TypedMessage { - if m != nil { - return m.ReceiverSettings - } - return nil -} - -func (m *InboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.TypedMessage { - if m != nil { - return m.ProxySettings - } - return nil -} - type OutboundConfig struct { } func (m *OutboundConfig) Reset() { *m = OutboundConfig{} } func (m *OutboundConfig) String() string { return proto.CompactTextString(m) } func (*OutboundConfig) ProtoMessage() {} -func (*OutboundConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } +func (*OutboundConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } type SenderConfig struct { // Send traffic through the given IP. Only IP is allowed. @@ -262,7 +229,7 @@ type SenderConfig struct { func (m *SenderConfig) Reset() { *m = SenderConfig{} } func (m *SenderConfig) String() string { return proto.CompactTextString(m) } func (*SenderConfig) ProtoMessage() {} -func (*SenderConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } +func (*SenderConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } func (m *SenderConfig) GetVia() *v2ray_core_common_net.IPOrDomain { if m != nil { @@ -292,54 +259,6 @@ func (m *SenderConfig) GetMultiplexSettings() *MultiplexingConfig { return nil } -type OutboundHandlerConfig struct { - Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` - SenderSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=sender_settings,json=senderSettings" json:"sender_settings,omitempty"` - ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"` - Expire int64 `protobuf:"varint,4,opt,name=expire" json:"expire,omitempty"` - Comment string `protobuf:"bytes,5,opt,name=comment" json:"comment,omitempty"` -} - -func (m *OutboundHandlerConfig) Reset() { *m = OutboundHandlerConfig{} } -func (m *OutboundHandlerConfig) String() string { return proto.CompactTextString(m) } -func (*OutboundHandlerConfig) ProtoMessage() {} -func (*OutboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } - -func (m *OutboundHandlerConfig) GetTag() string { - if m != nil { - return m.Tag - } - return "" -} - -func (m *OutboundHandlerConfig) GetSenderSettings() *v2ray_core_common_serial.TypedMessage { - if m != nil { - return m.SenderSettings - } - return nil -} - -func (m *OutboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.TypedMessage { - if m != nil { - return m.ProxySettings - } - return nil -} - -func (m *OutboundHandlerConfig) GetExpire() int64 { - if m != nil { - return m.Expire - } - return 0 -} - -func (m *OutboundHandlerConfig) GetComment() string { - if m != nil { - return m.Comment - } - return "" -} - type MultiplexingConfig struct { // Whether or not Mux is enabled. Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` @@ -350,7 +269,7 @@ type MultiplexingConfig struct { func (m *MultiplexingConfig) Reset() { *m = MultiplexingConfig{} } func (m *MultiplexingConfig) String() string { return proto.CompactTextString(m) } func (*MultiplexingConfig) ProtoMessage() {} -func (*MultiplexingConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } +func (*MultiplexingConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } func (m *MultiplexingConfig) GetEnabled() bool { if m != nil { @@ -372,10 +291,8 @@ func init() { proto.RegisterType((*AllocationStrategy_AllocationStrategyConcurrency)(nil), "v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency") proto.RegisterType((*AllocationStrategy_AllocationStrategyRefresh)(nil), "v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefresh") proto.RegisterType((*ReceiverConfig)(nil), "v2ray.core.app.proxyman.ReceiverConfig") - proto.RegisterType((*InboundHandlerConfig)(nil), "v2ray.core.app.proxyman.InboundHandlerConfig") proto.RegisterType((*OutboundConfig)(nil), "v2ray.core.app.proxyman.OutboundConfig") proto.RegisterType((*SenderConfig)(nil), "v2ray.core.app.proxyman.SenderConfig") - proto.RegisterType((*OutboundHandlerConfig)(nil), "v2ray.core.app.proxyman.OutboundHandlerConfig") proto.RegisterType((*MultiplexingConfig)(nil), "v2ray.core.app.proxyman.MultiplexingConfig") proto.RegisterEnum("v2ray.core.app.proxyman.KnownProtocols", KnownProtocols_name, KnownProtocols_value) proto.RegisterEnum("v2ray.core.app.proxyman.AllocationStrategy_Type", AllocationStrategy_Type_name, AllocationStrategy_Type_value) @@ -384,57 +301,49 @@ func init() { func init() { proto.RegisterFile("v2ray.com/core/app/proxyman/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 822 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xd1, 0x8e, 0xdb, 0x44, - 0x14, 0xad, 0xe3, 0x34, 0xc9, 0xde, 0xed, 0x7a, 0xdd, 0xa1, 0xd0, 0x10, 0x40, 0x0a, 0x01, 0xd1, - 0xa8, 0x20, 0xa7, 0xa4, 0xe2, 0x81, 0x27, 0x58, 0x76, 0x2b, 0x75, 0x81, 0x55, 0xcc, 0x24, 0xe2, - 0xa1, 0x42, 0xb2, 0x66, 0xed, 0xa9, 0x19, 0x61, 0xcf, 0x58, 0x33, 0x93, 0x74, 0xfd, 0x4b, 0x7c, - 0x05, 0x8f, 0x3c, 0xf0, 0x05, 0xfc, 0x0a, 0x2f, 0xc8, 0x9e, 0x71, 0x76, 0xb7, 0x49, 0x5a, 0x96, - 0xaa, 0x6f, 0x33, 0xc9, 0x39, 0xc7, 0x73, 0xcf, 0x3d, 0x77, 0x06, 0xc6, 0xab, 0xa9, 0x24, 0x65, - 0x10, 0x8b, 0x7c, 0x12, 0x0b, 0x49, 0x27, 0xa4, 0x28, 0x26, 0x85, 0x14, 0x17, 0x65, 0x4e, 0xf8, - 0x24, 0x16, 0xfc, 0x39, 0x4b, 0x83, 0x42, 0x0a, 0x2d, 0xd0, 0xfd, 0x06, 0x29, 0x69, 0x40, 0x8a, - 0x22, 0x68, 0x50, 0x83, 0x47, 0x2f, 0x49, 0xc4, 0x22, 0xcf, 0x05, 0x9f, 0x28, 0x2a, 0x19, 0xc9, - 0x26, 0xba, 0x2c, 0x68, 0x12, 0xe5, 0x54, 0x29, 0x92, 0x52, 0x23, 0x35, 0x78, 0xb0, 0x9d, 0xc1, - 0xa9, 0x9e, 0x90, 0x24, 0x91, 0x54, 0x29, 0x0b, 0xfc, 0x74, 0x37, 0xb0, 0x10, 0x52, 0x5b, 0x54, - 0xf0, 0x12, 0x4a, 0x4b, 0xc2, 0x55, 0xf5, 0xff, 0x84, 0x71, 0x4d, 0x65, 0x85, 0xbe, 0x5a, 0xc9, - 0xe8, 0x10, 0x0e, 0x4e, 0xf9, 0xb9, 0x58, 0xf2, 0xe4, 0xb8, 0xfe, 0x79, 0xf4, 0x87, 0x0b, 0xe8, - 0x28, 0xcb, 0x44, 0x4c, 0x34, 0x13, 0x7c, 0xae, 0x25, 0xd1, 0x34, 0x2d, 0xd1, 0x09, 0xb4, 0xab, - 0xd3, 0xf7, 0x9d, 0xa1, 0x33, 0xf6, 0xa6, 0x8f, 0x82, 0x1d, 0x06, 0x04, 0x9b, 0xd4, 0x60, 0x51, - 0x16, 0x14, 0xd7, 0x6c, 0xf4, 0x1b, 0xec, 0xc7, 0x82, 0xc7, 0x4b, 0x29, 0x29, 0x8f, 0xcb, 0x7e, - 0x6b, 0xe8, 0x8c, 0xf7, 0xa7, 0xa7, 0x37, 0x11, 0xdb, 0xfc, 0xe9, 0xf8, 0x52, 0x10, 0x5f, 0x55, - 0x47, 0x11, 0x74, 0x25, 0x7d, 0x2e, 0xa9, 0xfa, 0xb5, 0xef, 0xd6, 0x1f, 0x7a, 0xf2, 0x66, 0x1f, - 0xc2, 0x46, 0x0c, 0x37, 0xaa, 0x83, 0xaf, 0xe0, 0xa3, 0x57, 0x1e, 0x07, 0xdd, 0x83, 0xdb, 0x2b, - 0x92, 0x2d, 0x8d, 0x6b, 0x07, 0xd8, 0x6c, 0x06, 0x5f, 0xc2, 0xfb, 0x3b, 0xc5, 0xb7, 0x53, 0x46, - 0x5f, 0x40, 0xbb, 0x72, 0x11, 0x01, 0x74, 0x8e, 0xb2, 0x17, 0xa4, 0x54, 0xfe, 0xad, 0x6a, 0x8d, - 0x09, 0x4f, 0x44, 0xee, 0x3b, 0xe8, 0x0e, 0xf4, 0x9e, 0x5c, 0x54, 0xed, 0x25, 0x99, 0xdf, 0x1a, - 0xfd, 0xed, 0x82, 0x87, 0x69, 0x4c, 0xd9, 0x8a, 0x4a, 0xd3, 0x55, 0xf4, 0x0d, 0x40, 0x15, 0x82, - 0x48, 0x12, 0x9e, 0x1a, 0xed, 0xfd, 0xe9, 0xf0, 0xaa, 0x1d, 0x26, 0x4d, 0x01, 0xa7, 0x3a, 0x08, - 0x85, 0xd4, 0xb8, 0xc2, 0xe1, 0xbd, 0xa2, 0x59, 0xa2, 0xaf, 0xa1, 0x93, 0x31, 0xa5, 0x29, 0xb7, - 0x4d, 0xfb, 0x78, 0x07, 0xf9, 0x34, 0x9c, 0xc9, 0x13, 0x91, 0x13, 0xc6, 0xb1, 0x25, 0xa0, 0x5f, - 0xe0, 0x1d, 0xb2, 0xae, 0x37, 0x52, 0xb6, 0x60, 0xdb, 0x93, 0xcf, 0x6f, 0xd0, 0x13, 0x8c, 0xc8, - 0x66, 0x30, 0x17, 0x70, 0xa8, 0xb4, 0xa4, 0x24, 0x8f, 0x14, 0xd5, 0x9a, 0xf1, 0x54, 0xf5, 0xdb, - 0x9b, 0xca, 0xeb, 0x31, 0x08, 0x9a, 0x31, 0x08, 0xe6, 0x35, 0xcb, 0xf8, 0x83, 0x3d, 0xa3, 0x31, - 0xb7, 0x12, 0xe8, 0x5b, 0xf8, 0x50, 0x1a, 0x07, 0x23, 0x21, 0x59, 0xca, 0x38, 0xc9, 0xa2, 0x84, - 0x2a, 0xcd, 0x78, 0xfd, 0xf5, 0xfe, 0xed, 0xa1, 0x33, 0xee, 0xe1, 0x81, 0xc5, 0xcc, 0x2c, 0xe4, - 0xe4, 0x12, 0x81, 0x42, 0x38, 0x4c, 0x6a, 0x1f, 0x22, 0xb1, 0xa2, 0x52, 0xb2, 0x84, 0xf6, 0xbb, - 0x43, 0x77, 0xec, 0x4d, 0x1f, 0xec, 0xac, 0xf8, 0x07, 0x2e, 0x5e, 0xf0, 0xb0, 0x1a, 0xcb, 0x58, - 0x64, 0x0a, 0x7b, 0x86, 0x3f, 0xb3, 0xf4, 0xef, 0xdb, 0xbd, 0x8e, 0xdf, 0x1d, 0xfd, 0xe5, 0xc0, - 0x3d, 0x3b, 0xb1, 0x4f, 0x09, 0x4f, 0xb2, 0x75, 0x8b, 0x7d, 0x70, 0x35, 0x49, 0xeb, 0xde, 0xee, - 0xe1, 0x6a, 0x89, 0xe6, 0x70, 0xd7, 0x1e, 0x50, 0x5e, 0x9a, 0x63, 0xda, 0xf7, 0xd9, 0x96, 0xf6, - 0x99, 0x4b, 0xaa, 0x1e, 0xd7, 0xe4, 0xcc, 0xdc, 0x51, 0xd8, 0x6f, 0x04, 0xd6, 0xce, 0x9c, 0x81, - 0x57, 0x1f, 0xf8, 0x52, 0xd1, 0xbd, 0x91, 0xe2, 0x41, 0xcd, 0x6e, 0xe4, 0x46, 0x3e, 0x78, 0xb3, - 0xa5, 0xbe, 0x7a, 0x01, 0xfd, 0xd9, 0x82, 0x3b, 0x73, 0xca, 0x93, 0x75, 0x61, 0x8f, 0xc1, 0x5d, - 0x31, 0x62, 0x43, 0xfb, 0x1f, 0x72, 0x57, 0xa1, 0xb7, 0xc5, 0xa2, 0xf5, 0xe6, 0xb1, 0xf8, 0x69, - 0x47, 0xf1, 0x0f, 0x5f, 0x23, 0x1a, 0x56, 0x24, 0xab, 0x79, 0xdd, 0x00, 0xf4, 0x0c, 0x50, 0xbe, - 0xcc, 0x34, 0x2b, 0x32, 0x7a, 0xf1, 0xca, 0x08, 0x5f, 0x8b, 0xca, 0x59, 0x43, 0x61, 0x3c, 0xb5, - 0xba, 0x77, 0xd7, 0x32, 0x6b, 0x73, 0xff, 0x71, 0xe0, 0xdd, 0xc6, 0xdd, 0xd7, 0x85, 0x65, 0x06, - 0x87, 0xaa, 0x76, 0xfd, 0xff, 0x46, 0xc5, 0x33, 0xf4, 0xb7, 0x14, 0x14, 0xf4, 0x1e, 0x74, 0xe8, - 0x45, 0xc1, 0x24, 0xad, 0xbd, 0x71, 0xb1, 0xdd, 0xa1, 0x3e, 0x74, 0x2b, 0x11, 0xca, 0x75, 0x3d, - 0x94, 0x7b, 0xb8, 0xd9, 0x8e, 0x42, 0x40, 0x9b, 0x36, 0x55, 0x78, 0xca, 0xc9, 0x79, 0x46, 0x93, - 0xba, 0xfa, 0x1e, 0x6e, 0xb6, 0x68, 0xb8, 0xf9, 0x38, 0x1d, 0x5c, 0x7b, 0x51, 0x1e, 0x7e, 0x02, - 0xde, 0xf5, 0x19, 0x45, 0x3d, 0x68, 0x3f, 0x5d, 0x2c, 0x42, 0xff, 0x16, 0xea, 0x82, 0xbb, 0xf8, - 0x71, 0xee, 0x3b, 0xdf, 0x1d, 0xc3, 0x07, 0xb1, 0xc8, 0x77, 0x75, 0x2e, 0x74, 0x9e, 0xf5, 0x9a, - 0xf5, 0xef, 0xad, 0xfb, 0x3f, 0x4f, 0x31, 0x29, 0x83, 0xe3, 0x0a, 0x75, 0x54, 0x14, 0x26, 0x27, - 0x39, 0xe1, 0xe7, 0x9d, 0xfa, 0x75, 0x7e, 0xfc, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x25, 0xdf, - 0x6a, 0xb2, 0x93, 0x08, 0x00, 0x00, + // 691 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0x5d, 0x6f, 0xd3, 0x3a, + 0x1c, 0xc6, 0x97, 0xb6, 0x6b, 0x7b, 0xfe, 0x5b, 0xb3, 0x1c, 0x9f, 0x23, 0x2d, 0xa7, 0x07, 0xa4, + 0x52, 0x90, 0x56, 0x0d, 0x94, 0x40, 0x27, 0x2e, 0xb8, 0x82, 0xd1, 0x4d, 0x62, 0xbc, 0xa8, 0xc1, + 0xad, 0xb8, 0x98, 0x90, 0x22, 0x2f, 0xf1, 0x8a, 0x45, 0x62, 0x47, 0x8e, 0xdb, 0x2d, 0x5f, 0x89, + 0x4f, 0xc1, 0x25, 0x9f, 0x81, 0x4f, 0x83, 0xf2, 0xd6, 0x75, 0xeb, 0x3a, 0x98, 0x76, 0xe7, 0xa6, + 0xcf, 0xf3, 0xb3, 0xfd, 0x3c, 0xb6, 0xa1, 0x37, 0xeb, 0x4b, 0x92, 0x58, 0x9e, 0x08, 0x6d, 0x4f, + 0x48, 0x6a, 0x93, 0x28, 0xb2, 0x23, 0x29, 0xce, 0x93, 0x90, 0x70, 0xdb, 0x13, 0xfc, 0x94, 0x4d, + 0xac, 0x48, 0x0a, 0x25, 0xd0, 0x76, 0xa9, 0x94, 0xd4, 0x22, 0x51, 0x64, 0x95, 0xaa, 0xf6, 0xce, + 0x15, 0x84, 0x27, 0xc2, 0x50, 0x70, 0x9b, 0x53, 0x65, 0x13, 0xdf, 0x97, 0x34, 0x8e, 0x73, 0x42, + 0xfb, 0xd1, 0x6a, 0x61, 0x24, 0xa4, 0x2a, 0x54, 0xd6, 0x15, 0x95, 0x92, 0x84, 0xc7, 0xe9, 0xff, + 0x36, 0xe3, 0x8a, 0xca, 0x54, 0xbd, 0xb8, 0xae, 0xee, 0x16, 0xb4, 0x8e, 0xf8, 0x89, 0x98, 0x72, + 0x7f, 0x90, 0x7d, 0xee, 0x7e, 0xaf, 0x02, 0xda, 0x0f, 0x02, 0xe1, 0x11, 0xc5, 0x04, 0x1f, 0x29, + 0x49, 0x14, 0x9d, 0x24, 0xe8, 0x00, 0x6a, 0x2a, 0x89, 0xa8, 0xa9, 0x75, 0xb4, 0x9e, 0xde, 0x7f, + 0x6a, 0xad, 0xd8, 0x8e, 0xb5, 0x6c, 0xb5, 0xc6, 0x49, 0x44, 0x71, 0xe6, 0x46, 0x5f, 0x61, 0xc3, + 0x13, 0xdc, 0x9b, 0x4a, 0x49, 0xb9, 0x97, 0x98, 0x95, 0x8e, 0xd6, 0xdb, 0xe8, 0x1f, 0xdd, 0x06, + 0xb6, 0xfc, 0x69, 0x70, 0x01, 0xc4, 0x8b, 0x74, 0xe4, 0x42, 0x43, 0xd2, 0x53, 0x49, 0xe3, 0x2f, + 0x66, 0x35, 0x9b, 0xe8, 0xf0, 0x6e, 0x13, 0xe1, 0x1c, 0x86, 0x4b, 0x6a, 0xfb, 0x39, 0xdc, 0xbf, + 0x71, 0x39, 0xe8, 0x5f, 0x58, 0x9f, 0x91, 0x60, 0x9a, 0xa7, 0xd6, 0xc2, 0xf9, 0x8f, 0xf6, 0x33, + 0xf8, 0x6f, 0x25, 0xfc, 0x7a, 0x4b, 0xf7, 0x09, 0xd4, 0xd2, 0x14, 0x11, 0x40, 0x7d, 0x3f, 0x38, + 0x23, 0x49, 0x6c, 0xac, 0xa5, 0x63, 0x4c, 0xb8, 0x2f, 0x42, 0x43, 0x43, 0x9b, 0xd0, 0x3c, 0x3c, + 0x4f, 0xeb, 0x25, 0x81, 0x51, 0xe9, 0xfe, 0xac, 0x82, 0x8e, 0xa9, 0x47, 0xd9, 0x8c, 0xca, 0xbc, + 0x55, 0xf4, 0x12, 0x20, 0x3d, 0x04, 0xae, 0x24, 0x7c, 0x92, 0xb3, 0x37, 0xfa, 0x9d, 0xc5, 0x38, + 0xf2, 0xd3, 0x64, 0x71, 0xaa, 0x2c, 0x47, 0x48, 0x85, 0x53, 0x1d, 0xfe, 0x2b, 0x2a, 0x87, 0xe8, + 0x05, 0xd4, 0x03, 0x16, 0x2b, 0xca, 0x8b, 0xd2, 0x1e, 0xac, 0x30, 0x1f, 0x39, 0x43, 0x79, 0x20, + 0x42, 0xc2, 0x38, 0x2e, 0x0c, 0xe8, 0x33, 0xfc, 0x43, 0xe6, 0xfb, 0x75, 0xe3, 0x62, 0xc3, 0x45, + 0x27, 0x8f, 0x6f, 0xd1, 0x09, 0x46, 0x64, 0xf9, 0x60, 0x8e, 0x61, 0x2b, 0x56, 0x92, 0x92, 0xd0, + 0x8d, 0xa9, 0x52, 0x8c, 0x4f, 0x62, 0xb3, 0xb6, 0x4c, 0x9e, 0x5f, 0x03, 0xab, 0xbc, 0x06, 0xd6, + 0x28, 0x73, 0xe5, 0xf9, 0x60, 0x3d, 0x67, 0x8c, 0x0a, 0x04, 0x7a, 0x05, 0xf7, 0x64, 0x9e, 0xa0, + 0x2b, 0x24, 0x9b, 0x30, 0x4e, 0x02, 0xd7, 0xa7, 0xb1, 0x62, 0x3c, 0x9b, 0xdd, 0x5c, 0xef, 0x68, + 0xbd, 0x26, 0x6e, 0x17, 0x9a, 0x61, 0x21, 0x39, 0xb8, 0x50, 0x20, 0x07, 0xb6, 0xfc, 0x2c, 0x07, + 0x57, 0xcc, 0xa8, 0x94, 0xcc, 0xa7, 0x66, 0xa3, 0x53, 0xed, 0xe9, 0xfd, 0x9d, 0x95, 0x3b, 0x7e, + 0xc7, 0xc5, 0x19, 0x77, 0xd2, 0x6b, 0xe9, 0x89, 0x20, 0xc6, 0x7a, 0xee, 0x1f, 0x16, 0xf6, 0xb7, + 0xb5, 0x66, 0xdd, 0x68, 0x74, 0x0d, 0xd0, 0x87, 0x53, 0xb5, 0x78, 0x63, 0x7f, 0x54, 0x60, 0x73, + 0x44, 0xb9, 0x3f, 0x2f, 0x7b, 0x0f, 0xaa, 0x33, 0x46, 0x8a, 0x96, 0xff, 0xa0, 0xa8, 0x54, 0x7d, + 0x5d, 0x8e, 0x95, 0xbb, 0xe7, 0xf8, 0x11, 0xf4, 0x6c, 0x7b, 0x17, 0xd0, 0xbc, 0xf6, 0xdd, 0xdf, + 0x40, 0x9d, 0xd4, 0x54, 0x30, 0x5b, 0x19, 0x61, 0x8e, 0x3c, 0x06, 0x14, 0x4e, 0x03, 0xc5, 0xa2, + 0x80, 0x9e, 0xdf, 0xd8, 0xf9, 0xa5, 0x6c, 0x3f, 0x94, 0x16, 0xc6, 0x27, 0x05, 0xf7, 0xef, 0x39, + 0xa6, 0x64, 0x77, 0x1d, 0x40, 0xcb, 0x42, 0x64, 0x42, 0x83, 0x72, 0x72, 0x12, 0x50, 0x3f, 0xcb, + 0xb4, 0x89, 0xcb, 0x9f, 0xa8, 0xb3, 0xfc, 0x9e, 0xb5, 0x2e, 0x3d, 0x42, 0xbb, 0x0f, 0x41, 0xbf, + 0x5c, 0x2b, 0x6a, 0x42, 0xed, 0xcd, 0x78, 0xec, 0x18, 0x6b, 0xa8, 0x01, 0xd5, 0xf1, 0xfb, 0x91, + 0xa1, 0xbd, 0x1e, 0xc0, 0xff, 0x9e, 0x08, 0x57, 0xad, 0xdd, 0xd1, 0x8e, 0x9b, 0xe5, 0xf8, 0x5b, + 0x65, 0xfb, 0x53, 0x1f, 0x93, 0xc4, 0x1a, 0xa4, 0xaa, 0xfd, 0x28, 0xca, 0x93, 0x0a, 0x09, 0x3f, + 0xa9, 0x67, 0x0f, 0xfa, 0xde, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x77, 0x7f, 0xdc, 0x8e, 0x94, + 0x06, 0x00, 0x00, } diff --git a/app/proxyman/config.proto b/app/proxyman/config.proto index 6219db4fb..15f2171e0 100644 --- a/app/proxyman/config.proto +++ b/app/proxyman/config.proto @@ -6,7 +6,6 @@ option go_package = "proxyman"; option java_package = "com.v2ray.core.app.proxyman"; option java_multiple_files = true; -import "v2ray.com/core/common/serial/typed_message.proto"; import "v2ray.com/core/common/net/address.proto"; import "v2ray.com/core/common/net/port.proto"; import "v2ray.com/core/transport/internet/config.proto"; @@ -61,15 +60,6 @@ message ReceiverConfig { repeated KnownProtocols domain_override = 7; } -message InboundHandlerConfig { - // Tag of the inbound handler. - string tag = 1; - // Settings for how this inbound proxy is handled. Must be ReceiverConfig above. - v2ray.core.common.serial.TypedMessage receiver_settings = 2; - // Settings for inbound proxy. Must be one of the inbound proxies. - v2ray.core.common.serial.TypedMessage proxy_settings = 3; -} - message OutboundConfig { } @@ -82,19 +72,6 @@ message SenderConfig { MultiplexingConfig multiplex_settings = 4; } -message OutboundHandlerConfig { - // Tag of this outbound handler. - string tag = 1; - // Settings for how to dial connection for this outbound handler. Must be SenderConfig above. - v2ray.core.common.serial.TypedMessage sender_settings = 2; - // Settings for this outbound proxy. Must be one of the outbound proxies. - v2ray.core.common.serial.TypedMessage proxy_settings = 3; - // If not zero, this outbound will be expired in seconds. Not used for now. - int64 expire = 4; - // Comment of this outbound handler. Not used for now. - string comment = 5; -} - message MultiplexingConfig { // Whether or not Mux is enabled. bool enabled = 1; diff --git a/app/proxyman/errors.generated.go b/app/proxyman/errors.generated.go index b69ca4bc4..61b329269 100644 --- a/app/proxyman/errors.generated.go +++ b/app/proxyman/errors.generated.go @@ -2,6 +2,4 @@ package proxyman import "v2ray.com/core/common/errors" -func newError(values ...interface{}) *errors.Error { - return errors.New(values...).Path("App", "Proxyman") -} +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Proxyman") } diff --git a/app/proxyman/inbound/always.go b/app/proxyman/inbound/always.go index 4d20739f4..7a49ce592 100644 --- a/app/proxyman/inbound/always.go +++ b/app/proxyman/inbound/always.go @@ -14,6 +14,7 @@ type AlwaysOnInboundHandler struct { proxy proxy.Inbound workers []worker mux *mux.Server + tag string } func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) { @@ -25,6 +26,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig * h := &AlwaysOnInboundHandler{ proxy: p, mux: mux.NewServer(ctx), + tag: tag, } nl := p.Network() @@ -80,10 +82,14 @@ func (h *AlwaysOnInboundHandler) Close() { } } -func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (proxy.Inbound, net.Port, int) { +func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) { if len(h.workers) == 0 { return nil, 0, 0 } w := h.workers[dice.Roll(len(h.workers))] return w.Proxy(), w.Port(), 9999 } + +func (h *AlwaysOnInboundHandler) Tag() string { + return h.tag +} diff --git a/app/proxyman/inbound/dynamic.go b/app/proxyman/inbound/dynamic.go index ca1aa9d0c..281f8eb63 100644 --- a/app/proxyman/inbound/dynamic.go +++ b/app/proxyman/inbound/dynamic.go @@ -163,7 +163,7 @@ func (h *DynamicInboundHandler) Close() { h.cancel() } -func (h *DynamicInboundHandler) GetRandomInboundProxy() (proxy.Inbound, net.Port, int) { +func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) { h.workerMutex.RLock() defer h.workerMutex.RUnlock() @@ -174,3 +174,7 @@ func (h *DynamicInboundHandler) GetRandomInboundProxy() (proxy.Inbound, net.Port expire := h.receiverConfig.AllocationStrategy.GetRefreshValue() - uint32(time.Since(h.lastRefresh)/time.Minute) return w.Proxy(), w.Port(), int(expire) } + +func (h *DynamicInboundHandler) Tag() string { + return h.tag +} diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index bedf07b2f..f52a4cdae 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -5,64 +5,41 @@ package inbound import ( "context" + "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/common" ) // Manager is to manage all inbound handlers. type Manager struct { - handlers []proxyman.InboundHandler - taggedHandlers map[string]proxyman.InboundHandler + handlers []core.InboundHandler + taggedHandlers map[string]core.InboundHandler } func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) { - return &Manager{ - taggedHandlers: make(map[string]proxyman.InboundHandler), - }, nil + m := &Manager{ + taggedHandlers: make(map[string]core.InboundHandler), + } + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context") + } + if err := v.RegisterFeature((*core.InboundHandlerManager)(nil), m); err != nil { + return nil, newError("unable to register InboundHandlerManager").Base(err) + } + return m, nil } -func (m *Manager) AddHandler(ctx context.Context, config *proxyman.InboundHandlerConfig) error { - rawReceiverSettings, err := config.ReceiverSettings.GetInstance() - if err != nil { - return err - } - receiverSettings, ok := rawReceiverSettings.(*proxyman.ReceiverConfig) - if !ok { - return newError("not a ReceiverConfig").AtError() - } - proxySettings, err := config.ProxySettings.GetInstance() - if err != nil { - return err - } - var handler proxyman.InboundHandler - tag := config.Tag - allocStrategy := receiverSettings.AllocationStrategy - if allocStrategy == nil || allocStrategy.Type == proxyman.AllocationStrategy_Always { - h, err := NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings) - if err != nil { - return err - } - handler = h - } else if allocStrategy.Type == proxyman.AllocationStrategy_Random { - h, err := NewDynamicInboundHandler(ctx, tag, receiverSettings, proxySettings) - if err != nil { - return err - } - handler = h - } - - if handler == nil { - return newError("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError() - } - +func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) error { m.handlers = append(m.handlers, handler) + tag := handler.Tag() if len(tag) > 0 { m.taggedHandlers[tag] = handler } return nil } -func (m *Manager) GetHandler(ctx context.Context, tag string) (proxyman.InboundHandler, error) { +func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) { handler, found := m.taggedHandlers[tag] if !found { return nil, newError("handler not found: ", tag) @@ -85,12 +62,36 @@ func (m *Manager) Close() { } } -func (m *Manager) Interface() interface{} { - return (*proxyman.InboundHandlerManager)(nil) +func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (core.InboundHandler, error) { + rawReceiverSettings, err := config.ReceiverSettings.GetInstance() + if err != nil { + return nil, err + } + receiverSettings, ok := rawReceiverSettings.(*proxyman.ReceiverConfig) + if !ok { + return nil, newError("not a ReceiverConfig").AtError() + } + proxySettings, err := config.ProxySettings.GetInstance() + if err != nil { + return nil, err + } + tag := config.Tag + allocStrategy := receiverSettings.AllocationStrategy + if allocStrategy == nil || allocStrategy.Type == proxyman.AllocationStrategy_Always { + return NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings) + } + + if allocStrategy.Type == proxyman.AllocationStrategy_Random { + return NewDynamicInboundHandler(ctx, tag, receiverSettings, proxySettings) + } + return nil, newError("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError() } func init() { common.Must(common.RegisterConfig((*proxyman.InboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*proxyman.InboundConfig)) })) + common.Must(common.RegisterConfig((*core.InboundHandlerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return NewHandler(ctx, config.(*core.InboundHandlerConfig)) + })) } diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 6fce64c8b..24cfc2653 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -7,7 +7,7 @@ import ( "sync/atomic" "time" - "v2ray.com/core/app/dispatcher" + "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" @@ -31,7 +31,7 @@ type tcpWorker struct { stream *internet.StreamConfig recvOrigDest bool tag string - dispatcher dispatcher.Interface + dispatcher core.Dispatcher sniffers []proxyman.KnownProtocols ctx context.Context @@ -185,7 +185,7 @@ type udpWorker struct { port net.Port recvOrigDest bool tag string - dispatcher dispatcher.Interface + dispatcher core.Dispatcher ctx context.Context cancel context.CancelFunc diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index ca34c34c9..52b2b8851 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -8,8 +8,7 @@ import ( "sync" "time" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" + "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/common/buf" "v2ray.com/core/common/errors" @@ -262,21 +261,14 @@ func (m *Client) fetchOutput() { } type Server struct { - dispatcher dispatcher.Interface + dispatcher core.Dispatcher } // NewServer creates a new mux.Server. func NewServer(ctx context.Context) *Server { - s := &Server{} - space := app.SpaceFromContext(ctx) - space.On(app.SpaceInitializing, func(interface{}) error { - d := dispatcher.FromSpace(space) - if d == nil { - return newError("no dispatcher in space") - } - s.dispatcher = d - return nil - }) + s := &Server{ + dispatcher: core.FromContext(ctx).Dispatcher(), + } return s } @@ -295,8 +287,15 @@ func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (ray.Inboun return ray, nil } +func (s *Server) Start() error { + return nil +} + +func (s *Server) Close() { +} + type ServerWorker struct { - dispatcher dispatcher.Interface + dispatcher core.Dispatcher outboundRay ray.OutboundRay sessionManager *SessionManager } diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index 33e06d5e5..176b94a44 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -5,7 +5,7 @@ import ( "io" "time" - "v2ray.com/core/app" + "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/app/proxyman/mux" "v2ray.com/core/common/buf" @@ -17,29 +17,22 @@ import ( ) type Handler struct { - config *proxyman.OutboundHandlerConfig + config *core.OutboundHandlerConfig senderSettings *proxyman.SenderConfig proxy proxy.Outbound - outboundManager proxyman.OutboundHandlerManager + outboundManager core.OutboundHandlerManager mux *mux.ClientManager } -func NewHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) (*Handler, error) { +func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (*Handler, error) { + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context") + } h := &Handler{ - config: config, + config: config, + outboundManager: v.OutboundHandlerManager(), } - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context") - } - space.On(app.SpaceInitializing, func(interface{}) error { - ohm := proxyman.OutboundHandlerManagerFromSpace(space) - if ohm == nil { - return newError("no OutboundManager in space") - } - h.outboundManager = ohm - return nil - }) if config.SenderSettings != nil { senderSettings, err := config.SenderSettings.GetInstance() @@ -54,7 +47,12 @@ func NewHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) (*H } } - proxyHandler, err := config.GetProxyHandler(ctx) + proxyConfig, err := config.ProxySettings.GetInstance() + if err != nil { + return nil, err + } + + proxyHandler, err := proxy.CreateOutboundHandler(ctx, proxyConfig) if err != nil { return nil, err } @@ -71,6 +69,10 @@ func NewHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) (*H return h, nil } +func (h *Handler) Tag() string { + return h.config.Tag +} + // Dispatch implements proxy.Outbound.Dispatch. func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) { if h.mux != nil { diff --git a/app/proxyman/outbound/handler_test.go b/app/proxyman/outbound/handler_test.go new file mode 100644 index 000000000..109b087f0 --- /dev/null +++ b/app/proxyman/outbound/handler_test.go @@ -0,0 +1,15 @@ +package outbound_test + +import ( + "testing" + + "v2ray.com/core" + . "v2ray.com/core/app/proxyman/outbound" + . "v2ray.com/ext/assert" +) + +func TestInterfaces(t *testing.T) { + assert := With(t) + + assert((*Handler)(nil), Implements, (*core.OutboundHandler)(nil)) +} diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 5835cb0e3..6fb68866d 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -6,6 +6,7 @@ import ( "context" "sync" + "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/common" ) @@ -13,20 +14,23 @@ import ( // Manager is to manage all outbound handlers. type Manager struct { sync.RWMutex - defaultHandler *Handler - taggedHandler map[string]*Handler + defaultHandler core.OutboundHandler + taggedHandler map[string]core.OutboundHandler } // New creates a new Manager. func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) { - return &Manager{ - taggedHandler: make(map[string]*Handler), - }, nil -} - -// Interface implements Application.Interface. -func (*Manager) Interface() interface{} { - return (*proxyman.OutboundHandlerManager)(nil) + m := &Manager{ + taggedHandler: make(map[string]core.OutboundHandler), + } + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context") + } + if err := v.RegisterFeature((*core.OutboundHandlerManager)(nil), m); err != nil { + return nil, newError("unable to register OutboundHandlerManager").Base(err) + } + return m, nil } // Start implements Application.Start @@ -35,7 +39,7 @@ func (*Manager) Start() error { return nil } // Close implements Application.Close func (*Manager) Close() {} -func (m *Manager) GetDefaultHandler() proxyman.OutboundHandler { +func (m *Manager) GetDefaultHandler() core.OutboundHandler { m.RLock() defer m.RUnlock() if m.defaultHandler == nil { @@ -44,7 +48,7 @@ func (m *Manager) GetDefaultHandler() proxyman.OutboundHandler { return m.defaultHandler } -func (m *Manager) GetHandler(tag string) proxyman.OutboundHandler { +func (m *Manager) GetHandler(tag string) core.OutboundHandler { m.RLock() defer m.RUnlock() if handler, found := m.taggedHandler[tag]; found { @@ -53,20 +57,17 @@ func (m *Manager) GetHandler(tag string) proxyman.OutboundHandler { return nil } -func (m *Manager) AddHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) error { +func (m *Manager) AddHandler(ctx context.Context, handler core.OutboundHandler) error { m.Lock() defer m.Unlock() - handler, err := NewHandler(ctx, config) - if err != nil { - return err - } if m.defaultHandler == nil { m.defaultHandler = handler } - if len(config.Tag) > 0 { - m.taggedHandler[config.Tag] = handler + tag := handler.Tag() + if len(tag) > 0 { + m.taggedHandler[tag] = handler } return nil @@ -76,4 +77,7 @@ func init() { common.Must(common.RegisterConfig((*proxyman.OutboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*proxyman.OutboundConfig)) })) + common.Must(common.RegisterConfig((*core.OutboundHandlerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return NewHandler(ctx, config.(*core.OutboundHandlerConfig)) + })) } diff --git a/app/proxyman/proxyman.go b/app/proxyman/proxyman.go index f782a77b5..1ae393aa3 100644 --- a/app/proxyman/proxyman.go +++ b/app/proxyman/proxyman.go @@ -5,52 +5,8 @@ package proxyman import ( "context" - - "v2ray.com/core/app" - "v2ray.com/core/common/net" - "v2ray.com/core/proxy" - "v2ray.com/core/transport/ray" ) -type InboundHandlerManager interface { - GetHandler(ctx context.Context, tag string) (InboundHandler, error) - AddHandler(ctx context.Context, config *InboundHandlerConfig) error -} - -type InboundHandler interface { - Start() error - Close() - - // For migration - GetRandomInboundProxy() (proxy.Inbound, net.Port, int) -} - -type OutboundHandlerManager interface { - GetHandler(tag string) OutboundHandler - GetDefaultHandler() OutboundHandler - AddHandler(ctx context.Context, config *OutboundHandlerConfig) error -} - -type OutboundHandler interface { - Dispatch(ctx context.Context, outboundRay ray.OutboundRay) -} - -func InboundHandlerManagerFromSpace(space app.Space) InboundHandlerManager { - app := space.GetApplication((*InboundHandlerManager)(nil)) - if app == nil { - return nil - } - return app.(InboundHandlerManager) -} - -func OutboundHandlerManagerFromSpace(space app.Space) OutboundHandlerManager { - app := space.GetApplication((*OutboundHandlerManager)(nil)) - if app == nil { - return nil - } - return app.(OutboundHandlerManager) -} - type key int const ( diff --git a/app/router/router.go b/app/router/router.go index 17184a2d0..7d003dc4c 100644 --- a/app/router/router.go +++ b/app/router/router.go @@ -5,46 +5,47 @@ package router import ( "context" - "v2ray.com/core/app" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/net" "v2ray.com/core/proxy" ) -var ( - ErrNoRuleApplicable = newError("No rule applicable") -) - type Router struct { domainStrategy Config_DomainStrategy rules []Rule + dns core.DNSClient } func NewRouter(ctx context.Context, config *Config) (*Router, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context") + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context") } + r := &Router{ domainStrategy: config.DomainStrategy, rules: make([]Rule, len(config.Rule)), + dns: v.DNSClient(), } - space.On(app.SpaceInitializing, func(interface{}) error { - for idx, rule := range config.Rule { - r.rules[idx].Tag = rule.Tag - cond, err := rule.BuildCondition() - if err != nil { - return err - } - r.rules[idx].Condition = cond + for idx, rule := range config.Rule { + r.rules[idx].Tag = rule.Tag + cond, err := rule.BuildCondition() + if err != nil { + return nil, err } - return nil - }) + r.rules[idx].Condition = cond + } + + if err := v.RegisterFeature((*core.Router)(nil), r); err != nil { + return nil, newError("unable to register Router").Base(err) + } return r, nil } type ipResolver struct { + dns core.DNSClient ip []net.Address domain string resolved bool @@ -57,7 +58,7 @@ func (r *ipResolver) Resolve() []net.Address { newError("looking for IP for domain: ", r.domain).WriteToLog() r.resolved = true - ips, err := net.LookupIP(r.domain) + ips, err := r.dns.LookupIP(r.domain) if err != nil { newError("failed to get IP address").Base(err).WriteToLog() } @@ -71,8 +72,10 @@ func (r *ipResolver) Resolve() []net.Address { return r.ip } -func (r *Router) TakeDetour(ctx context.Context) (string, error) { - resolver := &ipResolver{} +func (r *Router) PickRoute(ctx context.Context) (string, error) { + resolver := &ipResolver{ + dns: r.dns, + } if r.domainStrategy == Config_IpOnDemand { if dest, ok := proxy.TargetFromContext(ctx); ok && dest.Address.Family().IsDomain() { resolver.domain = dest.Address.Domain() @@ -88,7 +91,7 @@ func (r *Router) TakeDetour(ctx context.Context) (string, error) { dest, ok := proxy.TargetFromContext(ctx) if !ok { - return "", ErrNoRuleApplicable + return "", core.ErrNoClue } if r.domainStrategy == Config_IpIfNonMatch && dest.Address.Family().IsDomain() { @@ -104,11 +107,7 @@ func (r *Router) TakeDetour(ctx context.Context) (string, error) { } } - return "", ErrNoRuleApplicable -} - -func (*Router) Interface() interface{} { - return (*Router)(nil) + return "", core.ErrNoClue } func (*Router) Start() error { @@ -117,14 +116,6 @@ func (*Router) Start() error { func (*Router) Close() {} -func FromSpace(space app.Space) *Router { - app := space.GetApplication((*Router)(nil)) - if app == nil { - return nil - } - return app.(*Router) -} - func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewRouter(ctx, config.(*Config)) diff --git a/app/router/router_test.go b/app/router/router_test.go index 855272cb7..856d5adb5 100644 --- a/app/router/router_test.go +++ b/app/router/router_test.go @@ -4,13 +4,14 @@ import ( "context" "testing" - "v2ray.com/core/app" + "v2ray.com/core" "v2ray.com/core/app/dispatcher" - _ "v2ray.com/core/app/dispatcher/impl" "v2ray.com/core/app/proxyman" _ "v2ray.com/core/app/proxyman/outbound" . "v2ray.com/core/app/router" + "v2ray.com/core/common" "v2ray.com/core/common/net" + "v2ray.com/core/common/serial" "v2ray.com/core/proxy" . "v2ray.com/ext/assert" ) @@ -18,28 +19,30 @@ import ( func TestSimpleRouter(t *testing.T) { assert := With(t) - config := &Config{ - Rule: []*RoutingRule{ - { - Tag: "test", - NetworkList: &net.NetworkList{ - Network: []net.Network{net.Network_TCP}, + config := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&Config{ + Rule: []*RoutingRule{ + { + Tag: "test", + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }, }, - }, + }), + serial.ToTypedMessage(&dispatcher.Config{}), + serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, } - space := app.NewSpace() - ctx := app.ContextWithSpace(context.Background(), space) - assert(app.AddApplicationToSpace(ctx, new(dispatcher.Config)), IsNil) - assert(app.AddApplicationToSpace(ctx, new(proxyman.OutboundConfig)), IsNil) - assert(app.AddApplicationToSpace(ctx, config), IsNil) - assert(space.Initialize(), IsNil) + v, err := core.New(config) + common.Must(err) - r := FromSpace(space) + r := v.Router() - ctx = proxy.ContextWithTarget(ctx, net.TCPDestination(net.DomainAddress("v2ray.com"), 80)) - tag, err := r.TakeDetour(ctx) + ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(net.DomainAddress("v2ray.com"), 80)) + tag, err := r.PickRoute(ctx) assert(err, IsNil) assert(tag, Equals, "test") } diff --git a/app/space.go b/app/space.go deleted file mode 100644 index 4a7bc22c4..000000000 --- a/app/space.go +++ /dev/null @@ -1,132 +0,0 @@ -package app - -import ( - "context" - "reflect" - - "v2ray.com/core/common" - "v2ray.com/core/common/event" -) - -// Application is a component that runs in Space. -type Application interface { - Interface() interface{} - Start() error - Close() -} - -// CreateAppFromConfig creates an Application based on its config. Application must have been registered. -func CreateAppFromConfig(ctx context.Context, config interface{}) (Application, error) { - application, err := common.CreateObject(ctx, config) - if err != nil { - return nil, err - } - switch a := application.(type) { - case Application: - return a, nil - default: - return nil, newError("not an application") - } -} - -// A Space contains all apps that may be available in a V2Ray runtime. -type Space interface { - event.Registry - GetApplication(appInterface interface{}) Application - AddApplication(application Application) error - Initialize() error - Start() error - Close() -} - -const ( - // SpaceInitializing is an event to be fired when Space is being initialized. - SpaceInitializing event.Event = iota -) - -type spaceImpl struct { - event.Listener - cache map[reflect.Type]Application - initialized bool -} - -// NewSpace creates a new Space. -func NewSpace() Space { - return &spaceImpl{ - cache: make(map[reflect.Type]Application), - } -} - -func (s *spaceImpl) On(e event.Event, h event.Handler) { - if e == SpaceInitializing && s.initialized { - _ = h(nil) // Ignore error - return - } - s.Listener.On(e, h) -} - -func (s *spaceImpl) Initialize() error { - if s.initialized { - return nil - } - s.initialized = true - return s.Fire(SpaceInitializing, nil) -} - -func (s *spaceImpl) GetApplication(appInterface interface{}) Application { - if s == nil { - return nil - } - appType := reflect.TypeOf(appInterface) - return s.cache[appType] -} - -func (s *spaceImpl) AddApplication(app Application) error { - if s == nil { - return newError("nil space").AtError() - } - appType := reflect.TypeOf(app.Interface()) - s.cache[appType] = app - return nil -} - -func (s *spaceImpl) Start() error { - for _, app := range s.cache { - if err := app.Start(); err != nil { - return err - } - } - return nil -} - -func (s *spaceImpl) Close() { - for _, app := range s.cache { - app.Close() - } -} - -type contextKey int - -const ( - spaceKey = contextKey(0) -) - -func AddApplicationToSpace(ctx context.Context, appConfig interface{}) error { - space := SpaceFromContext(ctx) - if space == nil { - return newError("no space in context").AtError() - } - application, err := CreateAppFromConfig(ctx, appConfig) - if err != nil { - return err - } - return space.AddApplication(application) -} - -func SpaceFromContext(ctx context.Context) Space { - return ctx.Value(spaceKey).(Space) -} - -func ContextWithSpace(ctx context.Context, space Space) context.Context { - return context.WithValue(ctx, spaceKey, space) -} diff --git a/common/interfaces.go b/common/interfaces.go new file mode 100644 index 000000000..6ce590c23 --- /dev/null +++ b/common/interfaces.go @@ -0,0 +1,10 @@ +package common + +// Runnable is the interface for objects that can start to work and stop on demand. +type Runnable interface { + // Start starts the runnable object. Upon the method returning nil, the object begins to function properly. + Start() error + + // Close stops the object being working. + Close() +} diff --git a/common/protocol/user.pb.go b/common/protocol/user.pb.go index 46c68a5fa..882c1542f 100644 --- a/common/protocol/user.pb.go +++ b/common/protocol/user.pb.go @@ -14,7 +14,7 @@ var _ = math.Inf type User struct { Level uint32 `protobuf:"varint,1,opt,name=level" json:"level,omitempty"` Email string `protobuf:"bytes,2,opt,name=email" json:"email,omitempty"` - // Protocol specific account information. + // Protocol specific account information. Must be the account proto in one of the proxies. Account *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=account" json:"account,omitempty"` } diff --git a/common/router/dispatcher.go b/common/router/dispatcher.go new file mode 100644 index 000000000..4934d3027 --- /dev/null +++ b/common/router/dispatcher.go @@ -0,0 +1,12 @@ +package router + +import ( + "context" + + "v2ray.com/core/common/net" + "v2ray.com/core/transport/ray" +) + +type Dispatcher interface { + Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error) +} diff --git a/common/router/router.go b/common/router/router.go new file mode 100644 index 000000000..a2dc15bbe --- /dev/null +++ b/common/router/router.go @@ -0,0 +1,53 @@ +package router + +import ( + "context" + "sync" +) + +type Router interface { + Pick(ctx context.Context) (string, bool) +} + +type defaultRouter byte + +func (defaultRouter) Pick(ctx context.Context) (string, bool) { + return "", false +} + +type syncRouter struct { + sync.RWMutex + Router +} + +func (r *syncRouter) Pick(ctx context.Context) (string, bool) { + r.RLock() + defer r.RUnlock() + + return r.Router.Pick(ctx) +} + +func (r *syncRouter) Set(router Router) { + r.Lock() + defer r.Unlock() + + r.Router = router +} + +var ( + routerInstance = &syncRouter{ + Router: defaultRouter(0), + } +) + +func RegisterRouter(router Router) { + if router == nil { + panic("Router is nil.") + } + + routerInstance.Set(router) +} + +func Pick(ctx context.Context) (string, bool) { + return routerInstance.Router.Pick(ctx) +} diff --git a/config.pb.go b/config.pb.go index 7c3426a54..1429f6e22 100644 --- a/config.pb.go +++ b/config.pb.go @@ -3,7 +3,6 @@ package core import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" -import v2ray_core_app_proxyman "v2ray.com/core/app/proxyman" import v2ray_core_common_serial "v2ray.com/core/common/serial" import v2ray_core_transport "v2ray.com/core/transport" @@ -40,12 +39,12 @@ func (x ConfigFormat) String() string { } func (ConfigFormat) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } -// Master config of V2Ray. V2Ray Core takes this config as input and functions accordingly. +// Master config of V2Ray. V2Ray takes this config as input and functions accordingly. type Config struct { // Inbound handler configurations. Must have at least one item. - Inbound []*v2ray_core_app_proxyman.InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"` + Inbound []*InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"` // Outbound handler configurations. Must have at least one item. The first item is used as default for routing. - Outbound []*v2ray_core_app_proxyman.OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound" json:"outbound,omitempty"` + Outbound []*OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound" json:"outbound,omitempty"` // App configuration. Must be one in the app directory. App []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,4,rep,name=app" json:"app,omitempty"` // Transport settings. @@ -60,14 +59,14 @@ func (m *Config) String() string { return proto.CompactTextString(m) func (*Config) ProtoMessage() {} func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } -func (m *Config) GetInbound() []*v2ray_core_app_proxyman.InboundHandlerConfig { +func (m *Config) GetInbound() []*InboundHandlerConfig { if m != nil { return m.Inbound } return nil } -func (m *Config) GetOutbound() []*v2ray_core_app_proxyman.OutboundHandlerConfig { +func (m *Config) GetOutbound() []*OutboundHandlerConfig { if m != nil { return m.Outbound } @@ -95,34 +94,131 @@ func (m *Config) GetExtension() []*v2ray_core_common_serial.TypedMessage { return nil } +type InboundHandlerConfig struct { + // Tag of the inbound handler. + Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` + // Settings for how this inbound proxy is handled. Must be ReceiverConfig above. + ReceiverSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings" json:"receiver_settings,omitempty"` + // Settings for inbound proxy. Must be one of the inbound proxies. + ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"` +} + +func (m *InboundHandlerConfig) Reset() { *m = InboundHandlerConfig{} } +func (m *InboundHandlerConfig) String() string { return proto.CompactTextString(m) } +func (*InboundHandlerConfig) ProtoMessage() {} +func (*InboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *InboundHandlerConfig) GetTag() string { + if m != nil { + return m.Tag + } + return "" +} + +func (m *InboundHandlerConfig) GetReceiverSettings() *v2ray_core_common_serial.TypedMessage { + if m != nil { + return m.ReceiverSettings + } + return nil +} + +func (m *InboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.TypedMessage { + if m != nil { + return m.ProxySettings + } + return nil +} + +type OutboundHandlerConfig struct { + // Tag of this outbound handler. + Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` + // Settings for how to dial connection for this outbound handler. Must be SenderConfig above. + SenderSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=sender_settings,json=senderSettings" json:"sender_settings,omitempty"` + // Settings for this outbound proxy. Must be one of the outbound proxies. + ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"` + // If not zero, this outbound will be expired in seconds. Not used for now. + Expire int64 `protobuf:"varint,4,opt,name=expire" json:"expire,omitempty"` + // Comment of this outbound handler. Not used for now. + Comment string `protobuf:"bytes,5,opt,name=comment" json:"comment,omitempty"` +} + +func (m *OutboundHandlerConfig) Reset() { *m = OutboundHandlerConfig{} } +func (m *OutboundHandlerConfig) String() string { return proto.CompactTextString(m) } +func (*OutboundHandlerConfig) ProtoMessage() {} +func (*OutboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *OutboundHandlerConfig) GetTag() string { + if m != nil { + return m.Tag + } + return "" +} + +func (m *OutboundHandlerConfig) GetSenderSettings() *v2ray_core_common_serial.TypedMessage { + if m != nil { + return m.SenderSettings + } + return nil +} + +func (m *OutboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.TypedMessage { + if m != nil { + return m.ProxySettings + } + return nil +} + +func (m *OutboundHandlerConfig) GetExpire() int64 { + if m != nil { + return m.Expire + } + return 0 +} + +func (m *OutboundHandlerConfig) GetComment() string { + if m != nil { + return m.Comment + } + return "" +} + func init() { proto.RegisterType((*Config)(nil), "v2ray.core.Config") + proto.RegisterType((*InboundHandlerConfig)(nil), "v2ray.core.InboundHandlerConfig") + proto.RegisterType((*OutboundHandlerConfig)(nil), "v2ray.core.OutboundHandlerConfig") proto.RegisterEnum("v2ray.core.ConfigFormat", ConfigFormat_name, ConfigFormat_value) } func init() { proto.RegisterFile("v2ray.com/core/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 336 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0xcd, 0x4a, 0xf3, 0x40, - 0x18, 0x85, 0xbf, 0xb4, 0xf9, 0x6a, 0xfa, 0xb6, 0x48, 0x99, 0x55, 0xa8, 0x2e, 0x8a, 0xd0, 0x52, - 0x04, 0x27, 0x12, 0x37, 0xe2, 0xd2, 0x8a, 0x3f, 0x05, 0x6d, 0xa9, 0xe2, 0xc2, 0x8d, 0x4c, 0xd3, - 0x69, 0x09, 0x74, 0xe6, 0x1d, 0x26, 0x53, 0x69, 0x6e, 0xc9, 0x9b, 0xf2, 0x56, 0x24, 0x99, 0xfe, - 0xa5, 0xe2, 0xc2, 0x55, 0x20, 0x73, 0x9e, 0xe7, 0x4c, 0x4e, 0xe0, 0xe8, 0x23, 0xd4, 0x2c, 0xa5, - 0x11, 0x8a, 0x20, 0x42, 0xcd, 0x83, 0x08, 0xe5, 0x34, 0x9e, 0x51, 0xa5, 0xd1, 0x20, 0x81, 0xf5, - 0xa1, 0xe6, 0xcd, 0xee, 0x5e, 0x90, 0x29, 0x15, 0x28, 0x8d, 0xcb, 0x54, 0x30, 0x59, 0xa0, 0x9a, - 0xe7, 0x3f, 0x94, 0x42, 0xa0, 0x0c, 0x12, 0xae, 0x63, 0x36, 0x0f, 0x4c, 0xaa, 0xf8, 0xe4, 0x5d, - 0xf0, 0x24, 0x61, 0x33, 0xbe, 0x22, 0xda, 0x7b, 0x84, 0xd1, 0x4c, 0x26, 0x0a, 0xb5, 0x29, 0x88, - 0x4f, 0xbe, 0x4a, 0x50, 0xe9, 0xe5, 0x2f, 0xc8, 0x1d, 0x1c, 0xc4, 0x72, 0x8c, 0x0b, 0x39, 0xf1, - 0x9d, 0x56, 0xb9, 0x5b, 0x0b, 0xcf, 0xe8, 0xf6, 0xae, 0x94, 0x29, 0x45, 0xd7, 0x77, 0xa3, 0x0f, - 0x36, 0x77, 0xcf, 0xe4, 0x64, 0xce, 0xb5, 0xe5, 0x47, 0x6b, 0x9a, 0xf4, 0xc1, 0xc3, 0x85, 0xb1, - 0xa6, 0x52, 0x6e, 0xa2, 0xbf, 0x9a, 0x06, 0xab, 0x60, 0x51, 0xb5, 0xe1, 0xc9, 0x25, 0x94, 0x99, - 0x52, 0xbe, 0x9b, 0x6b, 0x3a, 0xbb, 0x1a, 0x3b, 0x01, 0xb5, 0x13, 0xd0, 0x97, 0x6c, 0x82, 0x47, - 0xbb, 0xc0, 0x28, 0x43, 0xc8, 0x15, 0x54, 0x37, 0xdf, 0xec, 0xff, 0x6f, 0x39, 0xdd, 0x5a, 0x78, - 0xbc, 0xcb, 0x6f, 0x0e, 0xe9, 0xaa, 0x74, 0x1b, 0x27, 0x37, 0x50, 0xe5, 0x4b, 0xc3, 0x65, 0x12, - 0xa3, 0xf4, 0x2b, 0x7f, 0xea, 0xde, 0x82, 0x7d, 0xd7, 0x2b, 0x37, 0xdc, 0xd3, 0x0e, 0xd4, 0x6d, - 0xc1, 0x2d, 0x6a, 0xc1, 0x0c, 0xa9, 0x83, 0x37, 0xcc, 0xa6, 0x1f, 0x2f, 0xa6, 0x8d, 0x7f, 0xc4, - 0x03, 0xb7, 0xff, 0x3c, 0x78, 0x6a, 0x38, 0xd7, 0x6d, 0x38, 0x8c, 0x50, 0xec, 0xb4, 0x0c, 0x9d, - 0x37, 0x37, 0x7b, 0x7e, 0x96, 0xe0, 0x35, 0x1c, 0xb1, 0x94, 0xf6, 0x50, 0xf3, 0x71, 0x25, 0xff, - 0x6f, 0x17, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x62, 0x93, 0x6d, 0x78, 0x65, 0x02, 0x00, 0x00, + // 436 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x93, 0x41, 0x6f, 0xd3, 0x30, + 0x14, 0xc7, 0x71, 0x13, 0xba, 0xf6, 0x6d, 0x94, 0x60, 0x01, 0xb2, 0x06, 0x87, 0x50, 0x69, 0x53, + 0xc5, 0xc1, 0x45, 0xe5, 0x82, 0x26, 0x71, 0x61, 0x08, 0xc1, 0xa4, 0xd1, 0x29, 0x45, 0x1c, 0xb8, + 0x4c, 0x6e, 0xfa, 0x56, 0x45, 0x5a, 0xec, 0xc8, 0x76, 0xa7, 0xe6, 0x2b, 0xf1, 0x3d, 0xb8, 0xf1, + 0x8d, 0xb8, 0xa0, 0xc4, 0x49, 0x93, 0x41, 0x0f, 0x14, 0x69, 0xa7, 0xc4, 0x79, 0xfe, 0xfd, 0xdf, + 0xfb, 0x25, 0x31, 0x3c, 0xbb, 0x99, 0x68, 0x91, 0xf3, 0x58, 0xa5, 0xe3, 0x58, 0x69, 0x1c, 0xc7, + 0x4a, 0x5e, 0x25, 0x4b, 0x9e, 0x69, 0x65, 0x15, 0x85, 0xba, 0xa8, 0xf1, 0xf0, 0xd5, 0x5f, 0x1b, + 0xd3, 0x54, 0xc9, 0xb1, 0x41, 0x9d, 0x88, 0xeb, 0xb1, 0xcd, 0x33, 0x5c, 0x5c, 0xa6, 0x68, 0x8c, + 0x58, 0xa2, 0xa3, 0x0f, 0x8f, 0xfe, 0x20, 0xac, 0x16, 0xd2, 0x64, 0x4a, 0xdb, 0x5b, 0x4d, 0x86, + 0x3f, 0x3a, 0xd0, 0x3d, 0x2d, 0x1f, 0xd0, 0x13, 0xd8, 0x4b, 0xe4, 0x5c, 0xad, 0xe4, 0x82, 0x91, + 0xd0, 0x1b, 0xed, 0x4f, 0x42, 0xde, 0x4c, 0xc0, 0x3f, 0xb9, 0xd2, 0x47, 0x21, 0x17, 0xd7, 0xa8, + 0x1d, 0x12, 0xd5, 0x00, 0x7d, 0x0b, 0x3d, 0xb5, 0xb2, 0x0e, 0xee, 0x94, 0xf0, 0x8b, 0x36, 0x3c, + 0xad, 0x6a, 0xb7, 0xe9, 0x0d, 0x42, 0xdf, 0x80, 0x27, 0xb2, 0x8c, 0xf9, 0x25, 0x79, 0xdc, 0x26, + 0x9d, 0x28, 0x77, 0xa2, 0xfc, 0x4b, 0x21, 0x7a, 0xee, 0x3c, 0xa3, 0x02, 0xa1, 0x27, 0xd0, 0xdf, + 0x98, 0xb1, 0xfb, 0x21, 0x19, 0xed, 0x4f, 0x9e, 0xb7, 0xf9, 0x4d, 0x91, 0x57, 0x4d, 0x9b, 0xed, + 0xf4, 0x3d, 0xf4, 0x71, 0x6d, 0x51, 0x9a, 0x44, 0x49, 0xd6, 0xdd, 0xa9, 0x77, 0x03, 0x9e, 0xf9, + 0x3d, 0x2f, 0xf0, 0x87, 0x3f, 0x09, 0x3c, 0xde, 0xf6, 0x8a, 0x68, 0x00, 0x9e, 0x15, 0x4b, 0x46, + 0x42, 0x32, 0xea, 0x47, 0xc5, 0x2d, 0x9d, 0xc1, 0x23, 0x8d, 0x31, 0x26, 0x37, 0xa8, 0x2f, 0x0d, + 0x5a, 0x9b, 0xc8, 0xa5, 0x61, 0x9d, 0x72, 0xf4, 0x7f, 0x6d, 0x1f, 0xd4, 0x01, 0xb3, 0x8a, 0xa7, + 0xe7, 0x30, 0xc8, 0xb4, 0x5a, 0xe7, 0x4d, 0xa2, 0xb7, 0x53, 0xe2, 0x83, 0x92, 0xae, 0xe3, 0x86, + 0xbf, 0x08, 0x3c, 0xd9, 0xfa, 0xd1, 0xb6, 0xf8, 0x4c, 0xe1, 0xa1, 0x41, 0xb9, 0xf8, 0x7f, 0x9b, + 0x81, 0xc3, 0xef, 0xc8, 0x85, 0x3e, 0x85, 0x2e, 0xae, 0xb3, 0x44, 0x23, 0xf3, 0x43, 0x32, 0xf2, + 0xa2, 0x6a, 0x45, 0x19, 0xec, 0x15, 0x21, 0x28, 0xdd, 0x8f, 0xd3, 0x8f, 0xea, 0xe5, 0xcb, 0x63, + 0x38, 0x70, 0xb6, 0x1f, 0x94, 0x4e, 0x85, 0xa5, 0x07, 0xd0, 0xbb, 0x28, 0x4e, 0xcb, 0x7c, 0x75, + 0x15, 0xdc, 0xa3, 0x3d, 0xf0, 0xcf, 0x66, 0xd3, 0xcf, 0x01, 0x79, 0x77, 0x04, 0x83, 0x58, 0xa5, + 0xad, 0xa9, 0x2e, 0xc8, 0x37, 0xbf, 0xb8, 0x7e, 0xef, 0xc0, 0xd7, 0x49, 0x24, 0x72, 0x7e, 0xaa, + 0x34, 0xce, 0xbb, 0xe5, 0x51, 0x7b, 0xfd, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x59, 0x1b, + 0xee, 0x03, 0x00, 0x00, } diff --git a/config.proto b/config.proto index b5e337fc0..6101c0fa4 100644 --- a/config.proto +++ b/config.proto @@ -6,7 +6,6 @@ option go_package = "core"; option java_package = "com.v2ray.core"; option java_multiple_files = true; -import "v2ray.com/core/app/proxyman/config.proto"; import "v2ray.com/core/common/serial/typed_message.proto"; import "v2ray.com/core/transport/config.proto"; @@ -19,10 +18,10 @@ enum ConfigFormat { // Master config of V2Ray. V2Ray takes this config as input and functions accordingly. message Config { // Inbound handler configurations. Must have at least one item. - repeated v2ray.core.app.proxyman.InboundHandlerConfig inbound = 1; + repeated InboundHandlerConfig inbound = 1; // Outbound handler configurations. Must have at least one item. The first item is used as default for routing. - repeated v2ray.core.app.proxyman.OutboundHandlerConfig outbound = 2; + repeated OutboundHandlerConfig outbound = 2; reserved 3; @@ -36,3 +35,25 @@ message Config { // V2Ray will ignore such config during initialization. repeated v2ray.core.common.serial.TypedMessage extension = 6; } + +message InboundHandlerConfig { + // Tag of the inbound handler. + string tag = 1; + // Settings for how this inbound proxy is handled. Must be ReceiverConfig above. + v2ray.core.common.serial.TypedMessage receiver_settings = 2; + // Settings for inbound proxy. Must be one of the inbound proxies. + v2ray.core.common.serial.TypedMessage proxy_settings = 3; +} + +message OutboundHandlerConfig { + // Tag of this outbound handler. + string tag = 1; + // Settings for how to dial connection for this outbound handler. Must be SenderConfig above. + v2ray.core.common.serial.TypedMessage sender_settings = 2; + // Settings for this outbound proxy. Must be one of the outbound proxies. + v2ray.core.common.serial.TypedMessage proxy_settings = 3; + // If not zero, this outbound will be expired in seconds. Not used for now. + int64 expire = 4; + // Comment of this outbound handler. Not used for now. + string comment = 5; +} diff --git a/context.go b/context.go new file mode 100644 index 000000000..c92f0bacc --- /dev/null +++ b/context.go @@ -0,0 +1,17 @@ +package core + +import ( + "context" +) + +type key int + +const v2rayKey key = 1 + +// FromContext returns a Instance from the given context, or nil if the context doesn't contain one. +func FromContext(ctx context.Context) *Instance { + if s, ok := ctx.Value(v2rayKey).(*Instance); ok { + return s + } + return nil +} diff --git a/dns.go b/dns.go new file mode 100644 index 000000000..0c8046e19 --- /dev/null +++ b/dns.go @@ -0,0 +1,57 @@ +package core + +import "net" +import "sync" + +// DNSClient is a V2Ray feature for querying DNS information. +type DNSClient interface { + Feature + LookupIP(host string) ([]net.IP, error) +} + +type syncDNSClient struct { + sync.RWMutex + DNSClient +} + +func (d *syncDNSClient) LookupIP(host string) ([]net.IP, error) { + d.RLock() + defer d.RUnlock() + + if d.DNSClient == nil { + return net.LookupIP(host) + } + + return d.DNSClient.LookupIP(host) +} + +func (d *syncDNSClient) Start() error { + d.RLock() + defer d.RUnlock() + + if d.DNSClient == nil { + return nil + } + + return d.DNSClient.Start() +} + +func (d *syncDNSClient) Close() { + d.RLock() + defer d.RUnlock() + + if d.DNSClient != nil { + d.DNSClient.Close() + } +} + +func (d *syncDNSClient) Set(client DNSClient) { + if client == nil { + return + } + + d.Lock() + defer d.Unlock() + + d.DNSClient = client +} diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 85380d04e..1b55c1cad 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -2,10 +2,10 @@ package all import ( // The following are necessary as they register handlers in their init functions. - _ "v2ray.com/core/app/dispatcher/impl" + _ "v2ray.com/core/app/dispatcher" _ "v2ray.com/core/app/dns" _ "v2ray.com/core/app/log" - _ "v2ray.com/core/app/policy/manager" + _ "v2ray.com/core/app/policy" _ "v2ray.com/core/app/proxyman/inbound" _ "v2ray.com/core/app/proxyman/outbound" _ "v2ray.com/core/app/router" diff --git a/network.go b/network.go new file mode 100644 index 000000000..7524f7e4c --- /dev/null +++ b/network.go @@ -0,0 +1,165 @@ +package core + +import ( + "context" + "sync" + + "v2ray.com/core/common" + "v2ray.com/core/common/net" + "v2ray.com/core/transport/ray" +) + +// InboundHandler is the interface for handlers that process inbound connections. +type InboundHandler interface { + common.Runnable + // The tag of this handler. + Tag() string + + // Deprecated. Do not use in new code. + GetRandomInboundProxy() (interface{}, net.Port, int) +} + +// OutboundHandler is the interface for handlers that process outbound connections. +type OutboundHandler interface { + Tag() string + Dispatch(ctx context.Context, outboundRay ray.OutboundRay) +} + +// InboundHandlerManager is a feature that managers InboundHandlers. +type InboundHandlerManager interface { + Feature + // GetHandlers returns an InboundHandler for the given tag. + GetHandler(ctx context.Context, tag string) (InboundHandler, error) + // AddHandler adds the given handler into this InboundHandlerManager. + AddHandler(ctx context.Context, handler InboundHandler) error +} + +type syncInboundHandlerManager struct { + sync.RWMutex + InboundHandlerManager +} + +func (m *syncInboundHandlerManager) GetHandler(ctx context.Context, tag string) (InboundHandler, error) { + m.RLock() + defer m.RUnlock() + + if m.InboundHandlerManager == nil { + return nil, newError("InboundHandlerManager not set.").AtError() + } + + return m.InboundHandlerManager.GetHandler(ctx, tag) +} + +func (m *syncInboundHandlerManager) AddHandler(ctx context.Context, handler InboundHandler) error { + m.RLock() + defer m.RUnlock() + + if m.InboundHandlerManager == nil { + return newError("InboundHandlerManager not set.").AtError() + } + + return m.InboundHandlerManager.AddHandler(ctx, handler) +} + +func (m *syncInboundHandlerManager) Start() error { + m.RLock() + defer m.RUnlock() + + if m.InboundHandlerManager == nil { + return newError("InboundHandlerManager not set.").AtError() + } + + return m.InboundHandlerManager.Start() +} + +func (m *syncInboundHandlerManager) Close() { + m.RLock() + defer m.RUnlock() + + if m.InboundHandlerManager != nil { + m.InboundHandlerManager.Close() + } +} + +func (m *syncInboundHandlerManager) Set(manager InboundHandlerManager) { + m.Lock() + defer m.Unlock() + + m.InboundHandlerManager = manager +} + +// OutboundHandlerManager is a feature that manages OutboundHandlers. +type OutboundHandlerManager interface { + Feature + // GetHandler returns an OutboundHandler will given tag. + GetHandler(tag string) OutboundHandler + // GetDefaultHandler returns the default OutboundHandler. It is usually the first OutboundHandler specified in the configuration. + GetDefaultHandler() OutboundHandler + // AddHandler adds a handler into this OutboundHandlerManager. + AddHandler(ctx context.Context, handler OutboundHandler) error +} + +type syncOutboundHandlerManager struct { + sync.RWMutex + OutboundHandlerManager +} + +func (m *syncOutboundHandlerManager) GetHandler(tag string) OutboundHandler { + m.RLock() + defer m.RUnlock() + + if m.OutboundHandlerManager == nil { + return nil + } + + return m.OutboundHandlerManager.GetHandler(tag) +} + +func (m *syncOutboundHandlerManager) GetDefaultHandler() OutboundHandler { + m.RLock() + defer m.RUnlock() + + if m.OutboundHandlerManager == nil { + return nil + } + + return m.OutboundHandlerManager.GetDefaultHandler() +} + +func (m *syncOutboundHandlerManager) AddHandler(ctx context.Context, handler OutboundHandler) error { + m.RLock() + defer m.RUnlock() + + if m.OutboundHandlerManager == nil { + return newError("OutboundHandlerManager not set.").AtError() + } + + return m.OutboundHandlerManager.AddHandler(ctx, handler) +} + +func (m *syncOutboundHandlerManager) Start() error { + m.RLock() + defer m.RUnlock() + + if m.OutboundHandlerManager == nil { + return newError("OutboundHandlerManager not set.").AtError() + } + + return m.OutboundHandlerManager.Start() +} + +func (m *syncOutboundHandlerManager) Close() { + m.RLock() + defer m.RUnlock() + + if m.OutboundHandlerManager != nil { + m.OutboundHandlerManager.Close() + } +} + +func (m *syncOutboundHandlerManager) Set(manager OutboundHandlerManager) { + m.Lock() + defer m.Unlock() + + m.OutboundHandlerManager = manager +} diff --git a/policy.go b/policy.go new file mode 100644 index 000000000..12cff0f10 --- /dev/null +++ b/policy.go @@ -0,0 +1,117 @@ +package core + +import ( + "sync" + "time" +) + +// TimeoutPolicy contains limits for connection timeout. +type TimeoutPolicy struct { + // Timeout for handshake phase in a connection. + Handshake time.Duration + // Timeout for connection being idle, i.e., there is no egress or ingress traffic in this connection. + ConnectionIdle time.Duration + // Timeout for an uplink only connection, i.e., the downlink of the connection has ben closed. + UplinkOnly time.Duration + // Timeout for an downlink only connection, i.e., the uplink of the connection has ben closed. + DownlinkOnly time.Duration +} + +// OverrideWith overrides the current TimeoutPolicy with another one. All timeouts with zero value will be overridden with the new value. +func (p TimeoutPolicy) OverrideWith(another TimeoutPolicy) TimeoutPolicy { + if p.Handshake == 0 { + p.Handshake = another.Handshake + } + if p.ConnectionIdle == 0 { + p.ConnectionIdle = another.ConnectionIdle + } + if p.UplinkOnly == 0 { + p.UplinkOnly = another.UplinkOnly + } + if p.DownlinkOnly == 0 { + p.DownlinkOnly = another.DownlinkOnly + } + return p +} + +// Policy is session based settings for controlling V2Ray requests. It contains various settings (or limits) that may differ for different users in the context. +type Policy struct { + Timeouts TimeoutPolicy // Timeout settings +} + +// OverrideWith overrides the current Policy with another one. All values with default value will be overridden. +func (p Policy) OverrideWith(another Policy) Policy { + p.Timeouts.OverrideWith(another.Timeouts) + return p +} + +// PolicyManager is a feature that provides Policy for the given user by its id or level. +type PolicyManager interface { + Feature + + // ForLevel returns the Policy for the given user level. + ForLevel(level uint32) Policy +} + +// DefaultPolicy returns the Policy when user is not specified. +func DefaultPolicy() Policy { + return Policy{ + Timeouts: TimeoutPolicy{ + Handshake: time.Second * 4, + ConnectionIdle: time.Second * 300, + UplinkOnly: time.Second * 5, + DownlinkOnly: time.Second * 30, + }, + } +} + +type syncPolicyManager struct { + sync.RWMutex + PolicyManager +} + +func (m *syncPolicyManager) ForLevel(level uint32) Policy { + m.RLock() + defer m.RUnlock() + + if m.PolicyManager == nil { + p := DefaultPolicy() + if level == 1 { + p.Timeouts.ConnectionIdle = time.Second * 600 + } + return p + } + + return m.PolicyManager.ForLevel(level) +} + +func (m *syncPolicyManager) Start() error { + m.RLock() + defer m.RUnlock() + + if m.PolicyManager == nil { + return nil + } + + return m.PolicyManager.Start() +} + +func (m *syncPolicyManager) Close() { + m.RLock() + defer m.RUnlock() + + if m.PolicyManager != nil { + m.PolicyManager.Close() + } +} + +func (m *syncPolicyManager) Set(manager PolicyManager) { + if manager == nil { + return + } + + m.Lock() + defer m.Unlock() + + m.PolicyManager = manager +} diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index f56901d50..80e952390 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -4,10 +4,9 @@ package dokodemo import ( "context" + "time" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" - "v2ray.com/core/app/policy" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" @@ -18,36 +17,29 @@ import ( ) type DokodemoDoor struct { - config *Config - address net.Address - port net.Port - policy policy.Policy + policyManager core.PolicyManager + config *Config + address net.Address + port net.Port + v *core.Instance } func New(ctx context.Context, config *Config) (*DokodemoDoor, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context") - } if config.NetworkList == nil || config.NetworkList.Size() == 0 { return nil, newError("no network specified") } - d := &DokodemoDoor{ - config: config, - address: config.GetPredefinedAddress(), - port: net.Port(config.Port), + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context.") } - space.On(app.SpaceInitializing, func(interface{}) error { - pm := policy.FromSpace(space) - if pm == nil { - return newError("Policy not found in space.") - } - d.policy = pm.GetPolicy(config.UserLevel) - if config.Timeout > 0 && config.UserLevel == 0 { - d.policy.Timeout.ConnectionIdle.Value = config.Timeout - } - return nil - }) + + d := &DokodemoDoor{ + config: config, + address: config.GetPredefinedAddress(), + port: net.Port(config.Port), + policyManager: v.PolicyManager(), + } + return d, nil } @@ -55,7 +47,16 @@ func (d *DokodemoDoor) Network() net.NetworkList { return *(d.config.NetworkList) } -func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error { +func (d *DokodemoDoor) policy() core.Policy { + config := d.config + p := d.policyManager.ForLevel(config.UserLevel) + if config.Timeout > 0 && config.UserLevel == 0 { + p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second + } + return p +} + +func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error { newError("processing connection from: ", conn.RemoteAddr()).AtDebug().WriteToLog() dest := net.Destination{ Network: network, @@ -72,7 +73,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in } ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, d.policy.Timeout.ConnectionIdle.Duration()) + timer := signal.CancelAfterInactivity(ctx, cancel, d.policy().Timeouts.ConnectionIdle) inboundRay, err := dispatcher.Dispatch(ctx, dest) if err != nil { @@ -88,7 +89,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in return newError("failed to transport request").Base(err) } - timer.SetTimeout(d.policy.Timeout.DownlinkOnly.Duration()) + timer.SetTimeout(d.policy().Timeouts.DownlinkOnly) return nil }) @@ -115,7 +116,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in return newError("failed to transport response").Base(err) } - timer.SetTimeout(d.policy.Timeout.UplinkOnly.Duration()) + timer.SetTimeout(d.policy().Timeouts.UplinkOnly) return nil }) diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index c7294a527..ef87340f4 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -4,9 +4,9 @@ package freedom import ( "context" + "time" - "v2ray.com/core/app" - "v2ray.com/core/app/policy" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/dice" @@ -20,37 +20,35 @@ import ( // Handler handles Freedom connections. type Handler struct { - domainStrategy Config_DomainStrategy - timeout uint32 - destOverride *DestinationOverride - policy policy.Policy + policyManager core.PolicyManager + dns core.DNSClient + config Config } // New creates a new Freedom handler. func New(ctx context.Context, config *Config) (*Handler, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context") + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not found in context.") } + f := &Handler{ - domainStrategy: config.DomainStrategy, - timeout: config.Timeout, - destOverride: config.DestinationOverride, + config: *config, + policyManager: v.PolicyManager(), + dns: v.DNSClient(), } - space.On(app.SpaceInitializing, func(interface{}) error { - pm := policy.FromSpace(space) - if pm == nil { - return newError("Policy not found in space.") - } - f.policy = pm.GetPolicy(config.UserLevel) - if config.Timeout > 0 && config.UserLevel == 0 { - f.policy.Timeout.ConnectionIdle.Value = config.Timeout - } - return nil - }) + return f, nil } +func (h *Handler) policy() core.Policy { + p := h.policyManager.ForLevel(h.config.UserLevel) + if h.config.Timeout > 0 && h.config.UserLevel == 0 { + p.Timeouts.ConnectionIdle = time.Duration(h.config.Timeout) * time.Second + } + return p +} + func (h *Handler) resolveIP(ctx context.Context, domain string) net.Address { if resolver, ok := proxy.ResolvedIPsFromContext(ctx); ok { ips := resolver.Resolve() @@ -60,7 +58,7 @@ func (h *Handler) resolveIP(ctx context.Context, domain string) net.Address { return ips[dice.Roll(len(ips))] } - ips, err := net.LookupIP(domain) + ips, err := h.dns.LookupIP(domain) if err != nil { newError("failed to get IP address for domain ", domain).Base(err).WriteToLog() } @@ -73,8 +71,8 @@ func (h *Handler) resolveIP(ctx context.Context, domain string) net.Address { // Process implements proxy.Outbound. func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error { destination, _ := proxy.TargetFromContext(ctx) - if h.destOverride != nil { - server := h.destOverride.Server + if h.config.DestinationOverride != nil { + server := h.config.DestinationOverride.Server destination = net.Destination{ Network: destination.Network, Address: server.Address.AsAddress(), @@ -86,7 +84,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial input := outboundRay.OutboundInput() output := outboundRay.OutboundOutput() - if h.domainStrategy == Config_USE_IP && destination.Address.Family().IsDomain() { + if h.config.DomainStrategy == Config_USE_IP && destination.Address.Family().IsDomain() { ip := h.resolveIP(ctx, destination.Address.Domain()) if ip != nil { destination = net.Destination{ @@ -113,7 +111,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial defer conn.Close() ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, h.policy.Timeout.ConnectionIdle.Duration()) + timer := signal.CancelAfterInactivity(ctx, cancel, h.policy().Timeouts.ConnectionIdle) requestDone := signal.ExecuteAsync(func() error { var writer buf.Writer @@ -125,7 +123,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to process request").Base(err) } - timer.SetTimeout(h.policy.Timeout.DownlinkOnly.Duration()) + timer.SetTimeout(h.policy().Timeouts.DownlinkOnly) return nil }) @@ -136,7 +134,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial if err := buf.Copy(v2reader, output, buf.UpdateActivity(timer)); err != nil { return newError("failed to process response").Base(err) } - timer.SetTimeout(h.policy.Timeout.UplinkOnly.Duration()) + timer.SetTimeout(h.policy().Timeouts.UplinkOnly) return nil }) diff --git a/proxy/http/server.go b/proxy/http/server.go index 6841bfe7a..9179de132 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -10,9 +10,7 @@ import ( "strings" "time" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" - "v2ray.com/core/app/policy" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/errors" @@ -26,32 +24,31 @@ import ( // Server is a HTTP proxy server. type Server struct { config *ServerConfig - policy policy.Policy + v *core.Instance } // NewServer creates a new HTTP inbound handler. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context.") - } s := &Server{ config: config, + v: core.FromContext(ctx), } - space.On(app.SpaceInitializing, func(interface{}) error { - pm := policy.FromSpace(space) - if pm == nil { - return newError("Policy not found in space.") - } - s.policy = pm.GetPolicy(config.UserLevel) - if config.Timeout > 0 && config.UserLevel == 0 { - s.policy.Timeout.ConnectionIdle.Value = config.Timeout - } - return nil - }) + if s.v == nil { + return nil, newError("V is not in context.") + } + return s, nil } +func (s *Server) policy() core.Policy { + config := s.config + p := s.v.PolicyManager().ForLevel(config.UserLevel) + if config.Timeout > 0 && config.UserLevel == 0 { + p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second + } + return p +} + func (*Server) Network() net.NetworkList { return net.NetworkList{ Network: []net.Network{net.Network_TCP}, @@ -104,11 +101,11 @@ type readerOnly struct { io.Reader } -func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error { +func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error { reader := bufio.NewReaderSize(readerOnly{conn}, buf.Size) Start: - conn.SetReadDeadline(time.Now().Add(s.policy.Timeout.Handshake.Duration())) + conn.SetReadDeadline(time.Now().Add(s.policy().Timeouts.Handshake)) request, err := http.ReadRequest(reader) if err != nil { @@ -165,14 +162,14 @@ Start: return err } -func (s *Server) handleConnect(ctx context.Context, request *http.Request, reader *bufio.Reader, conn internet.Connection, dest net.Destination, dispatcher dispatcher.Interface) error { +func (s *Server) handleConnect(ctx context.Context, request *http.Request, reader *bufio.Reader, conn internet.Connection, dest net.Destination, dispatcher core.Dispatcher) error { _, err := conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) if err != nil { return newError("failed to write back OK response").Base(err) } ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, s.policy.Timeout.ConnectionIdle.Duration()) + timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle) ray, err := dispatcher.Dispatch(ctx, dest) if err != nil { return err @@ -191,7 +188,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade requestDone := signal.ExecuteAsync(func() error { defer ray.InboundInput().Close() - defer timer.SetTimeout(s.policy.Timeout.DownlinkOnly.Duration()) + defer timer.SetTimeout(s.policy().Timeouts.DownlinkOnly) v2reader := buf.NewReader(conn) return buf.Copy(v2reader, ray.InboundInput(), buf.UpdateActivity(timer)) @@ -202,7 +199,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade if err := buf.Copy(ray.InboundOutput(), v2writer, buf.UpdateActivity(timer)); err != nil { return err } - timer.SetTimeout(s.policy.Timeout.UplinkOnly.Duration()) + timer.SetTimeout(s.policy().Timeouts.UplinkOnly) return nil }) @@ -217,7 +214,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade var errWaitAnother = newError("keep alive") -func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, writer io.Writer, dest net.Destination, dispatcher dispatcher.Interface) error { +func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, writer io.Writer, dest net.Destination, dispatcher core.Dispatcher) error { if !s.config.AllowTransparent && len(request.URL.Host) <= 0 { // RFC 2068 (HTTP/1.1) requires URL to be absolute URL in HTTP proxy. response := &http.Response{ diff --git a/proxy/proxy.go b/proxy/proxy.go index ea359f630..50162378e 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -10,7 +10,7 @@ package proxy import ( "context" - "v2ray.com/core/app/dispatcher" + "v2ray.com/core" "v2ray.com/core/common/net" "v2ray.com/core/transport/internet" "v2ray.com/core/transport/ray" @@ -22,7 +22,7 @@ type Inbound interface { Network() net.NetworkList // Process processes a connection of given network. If necessary, the Inbound can dispatch the connection to an Outbound. - Process(context.Context, net.Network, internet.Connection, dispatcher.Interface) error + Process(context.Context, net.Network, internet.Connection, core.Dispatcher) error } // An Outbound process outbound connections. diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index 3d5d2be4d..ff19538e6 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -3,8 +3,7 @@ package shadowsocks import ( "context" - "v2ray.com/core/app" - "v2ray.com/core/app/policy" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" @@ -18,8 +17,8 @@ import ( // Client is a inbound handler for Shadowsocks protocol type Client struct { - serverPicker protocol.ServerPicker - policyManager policy.Manager + serverPicker protocol.ServerPicker + v *core.Instance } // NewClient create a new Shadowsocks client. @@ -33,19 +32,11 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { } client := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), + v: core.FromContext(ctx), } - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("Space not found.") + if client.v == nil { + return nil, newError("V is not in context.") } - space.On(app.SpaceInitializing, func(interface{}) error { - pm := policy.FromSpace(space) - if pm == nil { - return newError("Policy not found in space.") - } - client.policyManager = pm - return nil - }) return client, nil } @@ -103,9 +94,9 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale request.Option |= RequestOptionOneTimeAuth } - sessionPolicy := v.policyManager.GetPolicy(user.Level) + sessionPolicy := v.v.PolicyManager().ForLevel(user.Level) ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration()) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) if request.Command == protocol.RequestCommandTCP { bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) @@ -119,13 +110,13 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale } requestDone := signal.ExecuteAsync(func() error { - defer timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration()) + defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) return buf.Copy(outboundRay.OutboundInput(), bodyWriter, buf.UpdateActivity(timer)) }) responseDone := signal.ExecuteAsync(func() error { defer outboundRay.OutboundOutput().Close() - defer timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration()) + defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) responseReader, err := ReadTCPResponse(user, conn) if err != nil { diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index afb1812f1..31b345a07 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -4,9 +4,7 @@ import ( "context" "time" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" - "v2ray.com/core/app/policy" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/log" @@ -19,18 +17,14 @@ import ( ) type Server struct { - config *ServerConfig - user *protocol.User - account *MemoryAccount - policyManager policy.Manager + config *ServerConfig + user *protocol.User + account *MemoryAccount + v *core.Instance } // NewServer create a new Shadowsocks server. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context") - } if config.GetUser() == nil { return nil, newError("user is not specified") } @@ -45,16 +39,12 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { config: config, user: config.GetUser(), account: account, + v: core.FromContext(ctx), } - space.On(app.SpaceInitializing, func(interface{}) error { - pm := policy.FromSpace(space) - if pm == nil { - return newError("Policy not found in space.") - } - s.policyManager = pm - return nil - }) + if s.v == nil { + return nil, newError("V is not in context.") + } return s, nil } @@ -69,7 +59,7 @@ func (s *Server) Network() net.NetworkList { return list } -func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error { +func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error { switch network { case net.Network_TCP: return s.handleConnection(ctx, conn, dispatcher) @@ -80,7 +70,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet } } -func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error { +func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error { udpServer := udp.NewDispatcher(dispatcher) reader := buf.NewReader(conn) @@ -148,9 +138,9 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection return nil } -func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error { - sessionPolicy := s.policyManager.GetPolicy(s.user.Level) - conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeout.Handshake.Duration())) +func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error { + sessionPolicy := s.v.PolicyManager().ForLevel(s.user.Level) + conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)) bufferedReader := buf.NewBufferedReader(buf.NewReader(conn)) request, bodyReader, err := ReadTCPSession(s.user, bufferedReader) if err != nil { @@ -178,7 +168,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, ctx = protocol.ContextWithUser(ctx, request.User) ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration()) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) ray, err := dispatcher.Dispatch(ctx, dest) if err != nil { return err @@ -208,7 +198,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, return newError("failed to transport all TCP response").Base(err) } - timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration()) + timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) return nil }) @@ -219,7 +209,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, if err := buf.Copy(bodyReader, ray.InboundInput(), buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP request").Base(err) } - timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration()) + timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) return nil }) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 6c6670466..262ef4ad5 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -5,9 +5,7 @@ import ( "io" "time" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" - "v2ray.com/core/app/policy" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/log" @@ -22,32 +20,30 @@ import ( // Server is a SOCKS 5 proxy server type Server struct { config *ServerConfig - policy policy.Policy + v *core.Instance } // NewServer creates a new Server object. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context").AtWarning() - } s := &Server{ config: config, + v: core.FromContext(ctx), + } + if s.v == nil { + return nil, newError("V is not in context.") } - space.On(app.SpaceInitializing, func(interface{}) error { - pm := policy.FromSpace(space) - if pm == nil { - return newError("Policy not found in space.") - } - s.policy = pm.GetPolicy(config.UserLevel) - if config.Timeout > 0 && config.UserLevel == 0 { - s.policy.Timeout.ConnectionIdle.Value = config.Timeout - } - return nil - }) return s, nil } +func (s *Server) policy() core.Policy { + config := s.config + p := s.v.PolicyManager().ForLevel(config.UserLevel) + if config.Timeout > 0 && config.UserLevel == 0 { + p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second + } + return p +} + func (s *Server) Network() net.NetworkList { list := net.NetworkList{ Network: []net.Network{net.Network_TCP}, @@ -58,7 +54,7 @@ func (s *Server) Network() net.NetworkList { return list } -func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error { +func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error { switch network { case net.Network_TCP: return s.processTCP(ctx, conn, dispatcher) @@ -69,8 +65,8 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet } } -func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error { - conn.SetReadDeadline(time.Now().Add(s.policy.Timeout.Handshake.Duration())) +func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error { + conn.SetReadDeadline(time.Now().Add(s.policy().Timeouts.Handshake)) reader := buf.NewBufferedReader(buf.NewReader(conn)) inboundDest, ok := proxy.InboundEntryPointFromContext(ctx) @@ -125,9 +121,9 @@ func (*Server) handleUDP(c net.Conn) error { return err } -func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher dispatcher.Interface) error { +func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher core.Dispatcher) error { ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, v.policy.Timeout.ConnectionIdle.Duration()) + timer := signal.CancelAfterInactivity(ctx, cancel, v.policy().Timeouts.ConnectionIdle) ray, err := dispatcher.Dispatch(ctx, dest) if err != nil { @@ -144,7 +140,7 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ if err := buf.Copy(v2reader, input, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP request").Base(err) } - timer.SetTimeout(v.policy.Timeout.DownlinkOnly.Duration()) + timer.SetTimeout(v.policy().Timeouts.DownlinkOnly) return nil }) @@ -153,7 +149,7 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ if err := buf.Copy(output, v2writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP response").Base(err) } - timer.SetTimeout(v.policy.Timeout.UplinkOnly.Duration()) + timer.SetTimeout(v.policy().Timeouts.UplinkOnly) return nil }) @@ -166,7 +162,7 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ return nil } -func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error { +func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error { udpServer := udp.NewDispatcher(dispatcher) if source, ok := proxy.SourceFromContext(ctx); ok { diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 4b310f6c1..5021fd5b8 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -8,10 +8,7 @@ import ( "sync" "time" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" - "v2ray.com/core/app/policy" - "v2ray.com/core/app/proxyman" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/errors" @@ -74,21 +71,16 @@ func (v *userByEmail) Get(email string) (*protocol.User, bool) { // Handler is an inbound connection handler that handles messages in VMess protocol. type Handler struct { - inboundHandlerManager proxyman.InboundHandlerManager + policyManager core.PolicyManager + inboundHandlerManager core.InboundHandlerManager clients protocol.UserValidator usersByEmail *userByEmail detours *DetourConfig sessionHistory *encoding.SessionHistory - policyManager policy.Manager } // New creates a new VMess inbound handler. func New(ctx context.Context, config *Config) (*Handler, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context") - } - allowedClients := vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash) for _, user := range config.User { if err := allowedClients.Add(user); err != nil { @@ -96,24 +88,19 @@ func New(ctx context.Context, config *Config) (*Handler, error) { } } - handler := &Handler{ - clients: allowedClients, - detours: config.Detour, - usersByEmail: newUserByEmail(config.User, config.GetDefaultValue()), - sessionHistory: encoding.NewSessionHistory(ctx), + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context.") } - space.On(app.SpaceInitializing, func(interface{}) error { - handler.inboundHandlerManager = proxyman.InboundHandlerManagerFromSpace(space) - if handler.inboundHandlerManager == nil { - return newError("InboundHandlerManager is not found is space.") - } - handler.policyManager = policy.FromSpace(space) - if handler.policyManager == nil { - return newError("Policy is not found in space.") - } - return nil - }) + handler := &Handler{ + policyManager: v.PolicyManager(), + inboundHandlerManager: v.InboundHandlerManager(), + clients: allowedClients, + detours: config.Detour, + usersByEmail: newUserByEmail(config.User, config.GetDefaultValue()), + sessionHistory: encoding.NewSessionHistory(ctx), + } return handler, nil } @@ -179,9 +166,9 @@ func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSess } // Process implements proxy.Inbound.Process(). -func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher dispatcher.Interface) error { - sessionPolicy := h.policyManager.GetPolicy(0) - if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeout.Handshake.Duration())); err != nil { +func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher core.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() } @@ -221,11 +208,11 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i newError("unable to set back read deadline").Base(err).WriteToLog() } - sessionPolicy = h.policyManager.GetPolicy(request.User.Level) + sessionPolicy = h.policyManager.ForLevel(request.User.Level) ctx = protocol.ContextWithUser(ctx, request.User) ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration()) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) ray, err := dispatcher.Dispatch(ctx, request.Destination()) if err != nil { return newError("failed to dispatch request to ", request.Destination()).Base(err) @@ -235,14 +222,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i output := ray.InboundOutput() requestDone := signal.ExecuteAsync(func() error { - defer timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration()) + defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) return transferRequest(timer, session, request, reader, input) }) responseDone := signal.ExecuteAsync(func() error { writer := buf.NewBufferedWriter(buf.NewWriter(connection)) defer writer.Flush() - defer timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration()) + defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) response := &protocol.ResponseHeader{ Command: h.generateCommand(ctx, request), diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index 5cc5f818c..d195c8cc4 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -6,8 +6,7 @@ import ( "context" "time" - "v2ray.com/core/app" - "v2ray.com/core/app/policy" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" @@ -23,17 +22,12 @@ import ( // Handler is an outbound connection handler for VMess protocol. type Handler struct { - serverList *protocol.ServerList - serverPicker protocol.ServerPicker - policyManager policy.Manager + serverList *protocol.ServerList + serverPicker protocol.ServerPicker + v *core.Instance } func New(ctx context.Context, config *Config) (*Handler, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, newError("no space in context.") - } - serverList := protocol.NewServerList() for _, rec := range config.Receiver { serverList.AddServer(protocol.NewServerSpecFromPB(*rec)) @@ -41,16 +35,12 @@ func New(ctx context.Context, config *Config) (*Handler, error) { handler := &Handler{ serverList: serverList, serverPicker: protocol.NewRoundRobinServerPicker(serverList), + v: core.FromContext(ctx), } - space.On(app.SpaceInitializing, func(interface{}) error { - pm := policy.FromSpace(space) - if pm == nil { - return newError("Policy is not found in space.") - } - handler.policyManager = pm - return nil - }) + if handler.v == nil { + return nil, newError("V is not in context.") + } return handler, nil } @@ -112,10 +102,10 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial output := outboundRay.OutboundOutput() session := encoding.NewClientSession(protocol.DefaultIDHash) - sessionPolicy := v.policyManager.GetPolicy(request.User.Level) + sessionPolicy := v.v.PolicyManager().ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration()) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) requestDone := signal.ExecuteAsync(func() error { writer := buf.NewBufferedWriter(buf.NewWriter(conn)) @@ -148,13 +138,13 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial return err } } - timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration()) + timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) return nil }) responseDone := signal.ExecuteAsync(func() error { defer output.Close() - defer timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration()) + defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) reader := buf.NewBufferedReader(buf.NewReader(conn)) header, err := session.DecodeResponseHeader(reader) diff --git a/router.go b/router.go new file mode 100644 index 000000000..260806203 --- /dev/null +++ b/router.go @@ -0,0 +1,118 @@ +package core + +import ( + "context" + "sync" + + "v2ray.com/core/common/errors" + "v2ray.com/core/common/net" + "v2ray.com/core/transport/ray" +) + +// Dispatcher is a feature that dispatches inbound requests to outbound handlers based on rules. +// Dispatcher is required to be registered in a V2Ray instance to make V2Ray function properly. +type Dispatcher interface { + Feature + + // Dispatch returns a Ray for transporting data for the given request. + Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error) +} + +type syncDispatcher struct { + sync.RWMutex + Dispatcher +} + +func (d *syncDispatcher) Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error) { + d.RLock() + defer d.RUnlock() + + if d.Dispatcher == nil { + return nil, newError("Dispatcher not set.").AtError() + } + + return d.Dispatcher.Dispatch(ctx, dest) +} + +func (d *syncDispatcher) Start() error { + d.RLock() + defer d.RUnlock() + + if d.Dispatcher == nil { + return newError("Dispatcher not set.").AtError() + } + + return d.Dispatcher.Start() +} + +func (d *syncDispatcher) Close() { + d.RLock() + defer d.RUnlock() + + if d.Dispatcher != nil { + d.Dispatcher.Close() + } +} + +func (d *syncDispatcher) Set(disp Dispatcher) { + d.Lock() + defer d.Unlock() + + d.Dispatcher = disp +} + +var ( + // ErrNoClue is for the situation that existing information is not enough to make a decision. For example, Router may return this error when there is no suitable route. + ErrNoClue = errors.New("not enough information for making a decision") +) + +// Router is a feature to choose a outbound tag for the given request. +type Router interface { + Feature + + // PickRoute returns a tag of an OutboundHandler based on the given context. + PickRoute(ctx context.Context) (string, error) +} + +type syncRouter struct { + sync.RWMutex + Router +} + +func (r *syncRouter) PickRoute(ctx context.Context) (string, error) { + r.RLock() + defer r.RUnlock() + + if r.Router == nil { + return "", ErrNoClue + } + + return r.Router.PickRoute(ctx) +} + +func (r *syncRouter) Start() error { + r.RLock() + defer r.RUnlock() + + if r.Router == nil { + return nil + } + + return r.Router.Start() +} + +func (r *syncRouter) Close() { + r.RLock() + defer r.RUnlock() + + if r.Router != nil { + r.Router.Close() + } +} + +func (r *syncRouter) Set(router Router) { + r.Lock() + defer r.Unlock() + + r.Router = router +} diff --git a/testing/scenarios/common.go b/testing/scenarios/common.go index 6d054233f..ebfc663d3 100644 --- a/testing/scenarios/common.go +++ b/testing/scenarios/common.go @@ -13,10 +13,13 @@ import ( "github.com/golang/protobuf/proto" "v2ray.com/core" + "v2ray.com/core/app/dispatcher" + "v2ray.com/core/app/proxyman" "v2ray.com/core/common" "v2ray.com/core/common/log" "v2ray.com/core/common/net" "v2ray.com/core/common/retry" + "v2ray.com/core/common/serial" ) func pickPort() net.Port { @@ -70,6 +73,7 @@ func InitializeServerConfig(config *core.Config) (*exec.Cmd, error) { return nil, err } + config = withDefaultApps(config) configBytes, err := proto.Marshal(config) if err != nil { return nil, err @@ -128,3 +132,10 @@ func CloseAllServers(servers []*exec.Cmd) { Content: "All server closed.", }) } + +func withDefaultApps(config *core.Config) *core.Config { + config.App = append(config.App, serial.ToTypedMessage(&dispatcher.Config{})) + config.App = append(config.App, serial.ToTypedMessage(&proxyman.InboundConfig{})) + config.App = append(config.App, serial.ToTypedMessage(&proxyman.OutboundConfig{})) + return config +} diff --git a/testing/scenarios/dns_test.go b/testing/scenarios/dns_test.go index 665270776..5f168211d 100644 --- a/testing/scenarios/dns_test.go +++ b/testing/scenarios/dns_test.go @@ -51,7 +51,7 @@ func TestResolveIP(t *testing.T) { }, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -67,7 +67,7 @@ func TestResolveIP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, diff --git a/testing/scenarios/dokodemo_test.go b/testing/scenarios/dokodemo_test.go index 110584917..2a821f35b 100644 --- a/testing/scenarios/dokodemo_test.go +++ b/testing/scenarios/dokodemo_test.go @@ -40,7 +40,7 @@ func TestDokodemoTCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -57,7 +57,7 @@ func TestDokodemoTCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -73,7 +73,7 @@ func TestDokodemoTCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: &net.PortRange{From: clientPort, To: clientPort + clientPortRange}, @@ -88,7 +88,7 @@ func TestDokodemoTCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -147,7 +147,7 @@ func TestDokodemoUDP(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -164,7 +164,7 @@ func TestDokodemoUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -174,7 +174,7 @@ func TestDokodemoUDP(t *testing.T) { clientPort := uint32(pickPort()) clientPortRange := uint32(5) clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: &net.PortRange{From: clientPort, To: clientPort + clientPortRange}, @@ -189,7 +189,7 @@ func TestDokodemoUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ diff --git a/testing/scenarios/feature_test.go b/testing/scenarios/feature_test.go index 6c1513142..22c3ff727 100644 --- a/testing/scenarios/feature_test.go +++ b/testing/scenarios/feature_test.go @@ -44,7 +44,7 @@ func TestPassiveConnection(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -59,7 +59,7 @@ func TestPassiveConnection(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -115,7 +115,7 @@ func TestProxy(t *testing.T) { serverUserID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -132,7 +132,7 @@ func TestProxy(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -142,7 +142,7 @@ func TestProxy(t *testing.T) { proxyUserID := protocol.NewID(uuid.New()) proxyPort := pickPort() proxyConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(proxyPort), @@ -159,7 +159,7 @@ func TestProxy(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -168,7 +168,7 @@ func TestProxy(t *testing.T) { clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -183,7 +183,7 @@ func TestProxy(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -263,7 +263,7 @@ func TestProxyOverKCP(t *testing.T) { serverUserID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -283,7 +283,7 @@ func TestProxyOverKCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -293,7 +293,7 @@ func TestProxyOverKCP(t *testing.T) { proxyUserID := protocol.NewID(uuid.New()) proxyPort := pickPort() proxyConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(proxyPort), @@ -310,7 +310,7 @@ func TestProxyOverKCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ @@ -324,7 +324,7 @@ func TestProxyOverKCP(t *testing.T) { clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -339,7 +339,7 @@ func TestProxyOverKCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -429,7 +429,7 @@ func TestBlackhole(t *testing.T) { serverPort := pickPort() serverPort2 := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -457,7 +457,7 @@ func TestBlackhole(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { Tag: "direct", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), @@ -519,7 +519,7 @@ func TestForward(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -535,7 +535,7 @@ func TestForward(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{ DestinationOverride: &freedom.DestinationOverride{ @@ -585,7 +585,7 @@ func TestUDPConnection(t *testing.T) { clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -600,7 +600,7 @@ func TestUDPConnection(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -662,7 +662,7 @@ func TestDomainSniffing(t *testing.T) { sniffingPort := pickPort() httpPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { Tag: "snif", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ @@ -689,7 +689,7 @@ func TestDomainSniffing(t *testing.T) { ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { Tag: "redir", ProxySettings: serial.ToTypedMessage(&freedom.Config{ diff --git a/testing/scenarios/http_test.go b/testing/scenarios/http_test.go index 0e4752aef..942718e33 100644 --- a/testing/scenarios/http_test.go +++ b/testing/scenarios/http_test.go @@ -37,7 +37,7 @@ func TestHttpConformance(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -46,7 +46,7 @@ func TestHttpConformance(t *testing.T) { ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -92,7 +92,7 @@ func TestHttpConnectMethod(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -101,7 +101,7 @@ func TestHttpConnectMethod(t *testing.T) { ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -171,7 +171,7 @@ func TestHttpPost(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -180,7 +180,7 @@ func TestHttpPost(t *testing.T) { ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -237,7 +237,7 @@ func TestHttpBasicAuth(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -250,7 +250,7 @@ func TestHttpBasicAuth(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, diff --git a/testing/scenarios/shadowsocks_test.go b/testing/scenarios/shadowsocks_test.go index 931b64022..7f827bb9b 100644 --- a/testing/scenarios/shadowsocks_test.go +++ b/testing/scenarios/shadowsocks_test.go @@ -49,7 +49,7 @@ func TestShadowsocksAES256TCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -63,7 +63,7 @@ func TestShadowsocksAES256TCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -78,7 +78,7 @@ func TestShadowsocksAES256TCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -93,7 +93,7 @@ func TestShadowsocksAES256TCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ @@ -167,7 +167,7 @@ func TestShadowsocksAES128UDP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -182,7 +182,7 @@ func TestShadowsocksAES128UDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -197,7 +197,7 @@ func TestShadowsocksAES128UDP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -212,7 +212,7 @@ func TestShadowsocksAES128UDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ @@ -286,7 +286,7 @@ func TestShadowsocksChacha20TCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -300,7 +300,7 @@ func TestShadowsocksChacha20TCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -315,7 +315,7 @@ func TestShadowsocksChacha20TCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -330,7 +330,7 @@ func TestShadowsocksChacha20TCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ @@ -403,7 +403,7 @@ func TestShadowsocksAES256GCMTCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -417,7 +417,7 @@ func TestShadowsocksAES256GCMTCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -432,7 +432,7 @@ func TestShadowsocksAES256GCMTCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -447,7 +447,7 @@ func TestShadowsocksAES256GCMTCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ @@ -520,7 +520,7 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -535,7 +535,7 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -550,7 +550,7 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -565,7 +565,7 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ @@ -638,7 +638,7 @@ func TestShadowsocksAES256GCMConformance(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -652,7 +652,7 @@ func TestShadowsocksAES256GCMConformance(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -716,7 +716,7 @@ func TestShadowsocksChacha20Poly1305UDPConformance(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -731,7 +731,7 @@ func TestShadowsocksChacha20Poly1305UDPConformance(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -799,7 +799,7 @@ func TestShadowsocksChacha20Conformance(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -813,7 +813,7 @@ func TestShadowsocksChacha20Conformance(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, diff --git a/testing/scenarios/socks_test.go b/testing/scenarios/socks_test.go index 5351ad1c6..04ebe9660 100644 --- a/testing/scenarios/socks_test.go +++ b/testing/scenarios/socks_test.go @@ -30,7 +30,7 @@ func TestSocksBridgeTCP(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -46,7 +46,7 @@ func TestSocksBridgeTCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -55,7 +55,7 @@ func TestSocksBridgeTCP(t *testing.T) { clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -70,7 +70,7 @@ func TestSocksBridgeTCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&socks.ClientConfig{ Server: []*protocol.ServerEndpoint{ @@ -127,7 +127,7 @@ func TestSocksBridageUDP(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -143,7 +143,7 @@ func TestSocksBridageUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -152,7 +152,7 @@ func TestSocksBridageUDP(t *testing.T) { clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -167,7 +167,7 @@ func TestSocksBridageUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&socks.ClientConfig{ Server: []*protocol.ServerEndpoint{ @@ -225,7 +225,7 @@ func TestSocksConformance(t *testing.T) { authPort := pickPort() noAuthPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(authPort), @@ -255,7 +255,7 @@ func TestSocksConformance(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, diff --git a/testing/scenarios/tls_test.go b/testing/scenarios/tls_test.go index 76987d7a3..c6d5505d4 100644 --- a/testing/scenarios/tls_test.go +++ b/testing/scenarios/tls_test.go @@ -38,7 +38,7 @@ func TestSimpleTLSConnection(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -63,7 +63,7 @@ func TestSimpleTLSConnection(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -72,7 +72,7 @@ func TestSimpleTLSConnection(t *testing.T) { clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -87,7 +87,7 @@ func TestSimpleTLSConnection(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -152,7 +152,7 @@ func TestTLSOverKCP(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := udp.PickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -178,7 +178,7 @@ func TestTLSOverKCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -187,7 +187,7 @@ func TestTLSOverKCP(t *testing.T) { clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -202,7 +202,7 @@ func TestTLSOverKCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -268,7 +268,7 @@ func TestTLSOverWebSocket(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -294,7 +294,7 @@ func TestTLSOverWebSocket(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -303,7 +303,7 @@ func TestTLSOverWebSocket(t *testing.T) { clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -318,7 +318,7 @@ func TestTLSOverWebSocket(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ diff --git a/testing/scenarios/transport_test.go b/testing/scenarios/transport_test.go index a657366e8..253cd2806 100644 --- a/testing/scenarios/transport_test.go +++ b/testing/scenarios/transport_test.go @@ -35,7 +35,7 @@ func TestHttpConnectionHeader(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -62,7 +62,7 @@ func TestHttpConnectionHeader(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -71,7 +71,7 @@ func TestHttpConnectionHeader(t *testing.T) { clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -86,7 +86,7 @@ func TestHttpConnectionHeader(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ diff --git a/testing/scenarios/vmess_test.go b/testing/scenarios/vmess_test.go index 9b494fb91..c392d91f2 100644 --- a/testing/scenarios/vmess_test.go +++ b/testing/scenarios/vmess_test.go @@ -44,7 +44,7 @@ func TestVMessDynamicPort(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -84,7 +84,7 @@ func TestVMessDynamicPort(t *testing.T) { Tag: "detour", }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -99,7 +99,7 @@ func TestVMessDynamicPort(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -114,7 +114,7 @@ func TestVMessDynamicPort(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -179,7 +179,7 @@ func TestVMessGCM(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -197,7 +197,7 @@ func TestVMessGCM(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -212,7 +212,7 @@ func TestVMessGCM(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -227,7 +227,7 @@ func TestVMessGCM(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -302,7 +302,7 @@ func TestVMessGCMUDP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -320,7 +320,7 @@ func TestVMessGCMUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -335,7 +335,7 @@ func TestVMessGCMUDP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -350,7 +350,7 @@ func TestVMessGCMUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -435,7 +435,7 @@ func TestVMessChacha20(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -453,7 +453,7 @@ func TestVMessChacha20(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -468,7 +468,7 @@ func TestVMessChacha20(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -483,7 +483,7 @@ func TestVMessChacha20(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -558,7 +558,7 @@ func TestVMessNone(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -576,7 +576,7 @@ func TestVMessNone(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -591,7 +591,7 @@ func TestVMessNone(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -606,7 +606,7 @@ func TestVMessNone(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -683,7 +683,7 @@ func TestVMessKCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -704,7 +704,7 @@ func TestVMessKCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -719,7 +719,7 @@ func TestVMessKCP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -734,7 +734,7 @@ func TestVMessKCP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -816,7 +816,7 @@ func TestVMessIPv6(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -834,7 +834,7 @@ func TestVMessIPv6(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -849,7 +849,7 @@ func TestVMessIPv6(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -864,7 +864,7 @@ func TestVMessIPv6(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -931,7 +931,7 @@ func TestVMessGCMMux(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -949,7 +949,7 @@ func TestVMessGCMMux(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -964,7 +964,7 @@ func TestVMessGCMMux(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -979,7 +979,7 @@ func TestVMessGCMMux(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ MultiplexSettings: &proxyman.MultiplexingConfig{ @@ -1073,7 +1073,7 @@ func TestVMessGCMMuxUDP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), @@ -1091,7 +1091,7 @@ func TestVMessGCMMuxUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, @@ -1107,7 +1107,7 @@ func TestVMessGCMMuxUDP(t *testing.T) { ErrorLogType: log.LogType_Console, }), }, - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), @@ -1135,7 +1135,7 @@ func TestVMessGCMMuxUDP(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ MultiplexSettings: &proxyman.MultiplexingConfig{ diff --git a/transport/internet/config.pb.go b/transport/internet/config.pb.go index 80e4e5c21..ff89b9c73 100644 --- a/transport/internet/config.pb.go +++ b/transport/internet/config.pb.go @@ -46,7 +46,7 @@ func (TransportProtocol) EnumDescriptor() ([]byte, []int) { return fileDescripto type TransportConfig struct { // Type of network that this settings supports. Protocol TransportProtocol `protobuf:"varint,1,opt,name=protocol,enum=v2ray.core.transport.internet.TransportProtocol" json:"protocol,omitempty"` - // Specific settings. + // Specific settings. Must be of the transports. Settings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=settings" json:"settings,omitempty"` } @@ -74,7 +74,8 @@ type StreamConfig struct { Protocol TransportProtocol `protobuf:"varint,1,opt,name=protocol,enum=v2ray.core.transport.internet.TransportProtocol" json:"protocol,omitempty"` TransportSettings []*TransportConfig `protobuf:"bytes,2,rep,name=transport_settings,json=transportSettings" json:"transport_settings,omitempty"` // Type of security. Must be a message name of the settings proto. - SecurityType string `protobuf:"bytes,3,opt,name=security_type,json=securityType" json:"security_type,omitempty"` + SecurityType string `protobuf:"bytes,3,opt,name=security_type,json=securityType" json:"security_type,omitempty"` + // Settings for transport security. For now the only choice is TLS. SecuritySettings []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,4,rep,name=security_settings,json=securitySettings" json:"security_settings,omitempty"` } diff --git a/transport/internet/udp/dispatcher.go b/transport/internet/udp/dispatcher.go index 72cd30710..0db6ae54c 100644 --- a/transport/internet/udp/dispatcher.go +++ b/transport/internet/udp/dispatcher.go @@ -5,7 +5,7 @@ import ( "sync" "time" - "v2ray.com/core/app/dispatcher" + "v2ray.com/core" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" "v2ray.com/core/common/signal" @@ -23,10 +23,10 @@ type connEntry struct { type Dispatcher struct { sync.RWMutex conns map[net.Destination]*connEntry - dispatcher dispatcher.Interface + dispatcher core.Dispatcher } -func NewDispatcher(dispatcher dispatcher.Interface) *Dispatcher { +func NewDispatcher(dispatcher core.Dispatcher) *Dispatcher { return &Dispatcher{ conns: make(map[net.Destination]*connEntry), dispatcher: dispatcher, diff --git a/v2ray.go b/v2ray.go index f6fcb49ea..0bbb50e52 100644 --- a/v2ray.go +++ b/v2ray.go @@ -3,139 +3,160 @@ package core import ( "context" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" - "v2ray.com/core/app/policy" - "v2ray.com/core/app/proxyman" "v2ray.com/core/common" ) // Server is an instance of V2Ray. At any time, there must be at most one Server instance running. +// Deprecated. Use Instance directly. type Server interface { - // Start starts the V2Ray server, and return any error during the process. - // In the case of any errors, the state of the server is unpredicatable. - Start() error - - // Close closes the V2Ray server. All inbound and outbound connections will be closed immediately. - Close() + common.Runnable } -// New creates a new V2Ray server with given config. -func New(config *Config) (Server, error) { - return newSimpleServer(config) +// Feature is the interface for V2Ray features. All features must implement this interface. +// All existing features have an implementation in app directory. These features can be replaced by third-party ones. +type Feature interface { + common.Runnable } -// simpleServer shell of V2Ray. -type simpleServer struct { - space app.Space +// Instance combines all functionalities in V2Ray. +type Instance struct { + dnsClient syncDNSClient + policyManager syncPolicyManager + dispatcher syncDispatcher + router syncRouter + ihm syncInboundHandlerManager + ohm syncOutboundHandlerManager + + features []Feature } -// newSimpleServer returns a new Point server based on given configuration. -// The server is not started at this point. -func newSimpleServer(config *Config) (*simpleServer, error) { - var server = new(simpleServer) +// New returns a new V2Ray instance based on given configuration. +// The instance is not started at this point. +// To make sure V2Ray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional. +func New(config *Config) (*Instance, error) { + var server = new(Instance) if err := config.Transport.Apply(); err != nil { return nil, err } - space := app.NewSpace() - ctx := app.ContextWithSpace(context.Background(), space) - - server.space = space + ctx := context.WithValue(context.Background(), v2rayKey, server) for _, appSettings := range config.App { settings, err := appSettings.GetInstance() if err != nil { return nil, err } - application, err := app.CreateAppFromConfig(ctx, settings) + app, err := common.CreateObject(ctx, settings) if err != nil { return nil, err } - if err := space.AddApplication(application); err != nil { - return nil, err + f, ok := app.(Feature) + if !ok { + return nil, newError("not a feature") } - } - - outboundHandlerManager := proxyman.OutboundHandlerManagerFromSpace(space) - if outboundHandlerManager == nil { - o, err := app.CreateAppFromConfig(ctx, new(proxyman.OutboundConfig)) - if err != nil { - return nil, err - } - if err := space.AddApplication(o); err != nil { - return nil, newError("failed to add default outbound handler manager").Base(err) - } - outboundHandlerManager = o.(proxyman.OutboundHandlerManager) - } - - inboundHandlerManager := proxyman.InboundHandlerManagerFromSpace(space) - if inboundHandlerManager == nil { - o, err := app.CreateAppFromConfig(ctx, new(proxyman.InboundConfig)) - if err != nil { - return nil, err - } - if err := space.AddApplication(o); err != nil { - return nil, newError("failed to add default inbound handler manager").Base(err) - } - inboundHandlerManager = o.(proxyman.InboundHandlerManager) - } - - if disp := dispatcher.FromSpace(space); disp == nil { - d, err := app.CreateAppFromConfig(ctx, new(dispatcher.Config)) - if err != nil { - return nil, err - } - common.Must(space.AddApplication(d)) - } - - if p := policy.FromSpace(space); p == nil { - p, err := app.CreateAppFromConfig(ctx, &policy.Config{ - Level: map[uint32]*policy.Policy{ - 1: { - Timeout: &policy.Policy_Timeout{ - ConnectionIdle: &policy.Second{ - Value: 600, - }, - }, - }, - }, - }) - if err != nil { - return nil, err - } - common.Must(space.AddApplication(p)) + server.features = append(server.features, f) } for _, inbound := range config.Inbound { - if err := inboundHandlerManager.AddHandler(ctx, inbound); err != nil { + rawHandler, err := common.CreateObject(ctx, inbound) + if err != nil { + return nil, err + } + handler, ok := rawHandler.(InboundHandler) + if !ok { + return nil, newError("not an InboundHandler") + } + if err := server.InboundHandlerManager().AddHandler(ctx, handler); err != nil { return nil, err } } for _, outbound := range config.Outbound { - if err := outboundHandlerManager.AddHandler(ctx, outbound); err != nil { + rawHandler, err := common.CreateObject(ctx, outbound) + if err != nil { + return nil, err + } + handler, ok := rawHandler.(OutboundHandler) + if !ok { + return nil, newError("not an OutboundHandler") + } + if err := server.OutboundHandlerManager().AddHandler(ctx, handler); err != nil { return nil, err } - } - - if err := server.space.Initialize(); err != nil { - return nil, err } return server, nil } -func (s *simpleServer) Close() { - s.space.Close() +// Close shutdown the V2Ray instance. +func (s *Instance) Close() { + for _, f := range s.features { + f.Close() + } } -func (s *simpleServer) Start() error { - if err := s.space.Start(); err != nil { - return err +// Start starts the V2Ray instance, including all registered features. When Start returns error, the state of the instance is unknown. +func (s *Instance) Start() error { + for _, f := range s.features { + if err := f.Start(); err != nil { + return nil + } } + newError("V2Ray started").AtWarning().WriteToLog() return nil } + +// RegisterFeature registers the given feature into V2Ray. +// If feature is one of the following types, the corressponding feature in this Instance +// will be replaced: DNSClient, PolicyManager, Router, Dispatcher, InboundHandlerManager, OutboundHandlerManager. +func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error { + switch feature.(type) { + case DNSClient, *DNSClient: + s.dnsClient.Set(instance.(DNSClient)) + case PolicyManager, *PolicyManager: + s.policyManager.Set(instance.(PolicyManager)) + case Router, *Router: + s.router.Set(instance.(Router)) + case Dispatcher, *Dispatcher: + s.dispatcher.Set(instance.(Dispatcher)) + case InboundHandlerManager, *InboundHandlerManager: + s.ihm.Set(instance.(InboundHandlerManager)) + case OutboundHandlerManager, *OutboundHandlerManager: + s.ohm.Set(instance.(OutboundHandlerManager)) + } + s.features = append(s.features, instance) + return nil +} + +// DNSClient returns the DNSClient used by this Instance. The returned DNSClient is always functional. +func (s *Instance) DNSClient() DNSClient { + return &(s.dnsClient) +} + +// PolicyManager returns the PolicyManager used by this Instance. The returned PolicyManager is always functional. +func (s *Instance) PolicyManager() PolicyManager { + return &(s.policyManager) +} + +// Router returns the Router used by this Instance. The returned Router is always functional. +func (s *Instance) Router() Router { + return &(s.router) +} + +// Dispatcher returns the Dispatcher used by this Instance. If Dispatcher was not registered before, the returned value doesn't work, although it is not nil. +func (s *Instance) Dispatcher() Dispatcher { + return &(s.dispatcher) +} + +// InboundHandlerManager returns the InboundHandlerManager used by this Instance. If InboundHandlerManager was not registered before, the returned value doesn't work. +func (s *Instance) InboundHandlerManager() InboundHandlerManager { + return &(s.ihm) +} + +// OutboundHandlerManager returns the OutboundHandlerManager used by this Instance. If OutboundHandlerManager was not registered before, the returned value doesn't work. +func (s *Instance) OutboundHandlerManager() OutboundHandlerManager { + return &(s.ohm) +} diff --git a/v2ray_test.go b/v2ray_test.go index 4374340c3..6d18c62b1 100644 --- a/v2ray_test.go +++ b/v2ray_test.go @@ -22,7 +22,7 @@ func TestV2RayClose(t *testing.T) { port := net.Port(dice.RollUint16()) config := &Config{ - Inbound: []*proxyman.InboundHandlerConfig{ + Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(port), @@ -37,7 +37,7 @@ func TestV2RayClose(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ From e4554e58716f5c273e2adab429d4213c1993ce74 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 10 Jan 2018 12:59:45 +0100 Subject: [PATCH 03/63] fix test break --- v2ray_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/v2ray_test.go b/v2ray_test.go index 6d18c62b1..51f120c23 100644 --- a/v2ray_test.go +++ b/v2ray_test.go @@ -22,7 +22,11 @@ func TestV2RayClose(t *testing.T) { port := net.Port(dice.RollUint16()) config := &Config{ - Inbound: []*core.InboundHandlerConfig{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&proxyman.InboundConfig{}), + serial.ToTypedMessage(&proxyman.OutboundConfig{}), + }, + Inbound: []*InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(port), @@ -37,7 +41,7 @@ func TestV2RayClose(t *testing.T) { }), }, }, - Outbound: []*core.OutboundHandlerConfig{ + Outbound: []*OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ From ec54b0453734879af132745cd4aaee9db82409f8 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 10 Jan 2018 13:07:56 +0100 Subject: [PATCH 04/63] fix test break --- transport/internet/udp/dispatcher_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/transport/internet/udp/dispatcher_test.go b/transport/internet/udp/dispatcher_test.go index 76a939e77..989cb6510 100644 --- a/transport/internet/udp/dispatcher_test.go +++ b/transport/internet/udp/dispatcher_test.go @@ -21,6 +21,12 @@ func (d *TestDispatcher) Dispatch(ctx context.Context, dest net.Destination) (ra return d.OnDispatch(ctx, dest) } +func (d *TestDispatcher) Start() error { + return nil +} + +func (d *TestDispatcher) Close() {} + func TestSameDestinationDispatching(t *testing.T) { assert := With(t) From 7d2c34f67440a753362f0ddb12bbfdbd6610ea85 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 10 Jan 2018 13:30:57 +0100 Subject: [PATCH 05/63] remove unused code --- common/net/dns.go | 44 -------------------------------------------- common/net/system.go | 2 ++ 2 files changed, 2 insertions(+), 44 deletions(-) delete mode 100644 common/net/dns.go diff --git a/common/net/dns.go b/common/net/dns.go deleted file mode 100644 index 52ef74364..000000000 --- a/common/net/dns.go +++ /dev/null @@ -1,44 +0,0 @@ -package net - -import ( - "net" - "sync/atomic" - "unsafe" -) - -// IPResolver is the interface to resolve host name to IPs. -type IPResolver interface { - LookupIP(host string) ([]net.IP, error) -} - -type systemIPResolver int - -func (s systemIPResolver) LookupIP(host string) ([]net.IP, error) { - return net.LookupIP(host) -} - -const ( - systemIPResolverInstance = systemIPResolver(0) -) - -// SystemIPResolver returns an IPResolver that resolves IP through underlying system. -func SystemIPResolver() IPResolver { - return systemIPResolverInstance -} - -var ( - ipResolver unsafe.Pointer -) - -func LookupIP(host string) ([]net.IP, error) { - r := (*IPResolver)(atomic.LoadPointer(&ipResolver)) - return (*r).LookupIP(host) -} - -func RegisterIPResolver(resolver IPResolver) { - atomic.StorePointer(&ipResolver, unsafe.Pointer(&resolver)) -} - -func init() { - RegisterIPResolver(systemIPResolverInstance) -} diff --git a/common/net/system.go b/common/net/system.go index 33dc86b6a..b2ffbf2f8 100644 --- a/common/net/system.go +++ b/common/net/system.go @@ -11,6 +11,8 @@ var Listen = net.Listen var ListenTCP = net.ListenTCP var ListenUDP = net.ListenUDP +var LookupIP = net.LookupIP + var FileConn = net.FileConn var ParseIP = net.ParseIP From 6a7887b6556cc49d3f503776309328e74c8fcb63 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 10 Jan 2018 13:32:48 +0100 Subject: [PATCH 06/63] fix LocalNameServer --- app/dns/nameserver.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index aef28064f..a689e785c 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -216,8 +216,7 @@ func (*LocalNameServer) QueryA(domain string) <-chan *ARecord { go func() { defer close(response) - resolver := net.SystemIPResolver() - ips, err := resolver.LookupIP(domain) + ips, err := net.LookupIP(domain) if err != nil { newError("failed to lookup IPs for domain ", domain).Base(err).AtWarning().WriteToLog() return From 68ef98a35dc4d299f4c2e3892c3117d577cb75d2 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 10 Jan 2018 14:47:12 +0100 Subject: [PATCH 07/63] remove unused code --- app/log/log.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/log/log.go b/app/log/log.go index 9518f722a..5614da2b1 100644 --- a/app/log/log.go +++ b/app/log/log.go @@ -37,11 +37,6 @@ func New(ctx context.Context, config *Config) (*Instance, error) { return g, nil } -// Interface implements app.Application.Interface(). -func (*Instance) Interface() interface{} { - return (*Instance)(nil) -} - func (g *Instance) initAccessLogger() error { switch g.config.AccessLogType { case LogType_File: From c45b24c86181031ae1011dd5aa990764593c1425 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 10 Jan 2018 14:47:23 +0100 Subject: [PATCH 08/63] fix creation logic --- v2ray.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/v2ray.go b/v2ray.go index 0bbb50e52..dca5cf9a8 100644 --- a/v2ray.go +++ b/v2ray.go @@ -47,15 +47,9 @@ func New(config *Config) (*Instance, error) { if err != nil { return nil, err } - app, err := common.CreateObject(ctx, settings) - if err != nil { + if _, err := common.CreateObject(ctx, settings); err != nil { return nil, err } - f, ok := app.(Feature) - if !ok { - return nil, newError("not a feature") - } - server.features = append(server.features, f) } for _, inbound := range config.Inbound { @@ -100,7 +94,7 @@ func (s *Instance) Close() { func (s *Instance) Start() error { for _, f := range s.features { if err := f.Start(); err != nil { - return nil + return err } } From efc8c23207b6cf14217a74b8f9668f7c88135661 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 10 Jan 2018 17:31:52 +0100 Subject: [PATCH 09/63] support dial for v2ray instance --- app/proxyman/outbound/handler.go | 102 +------------------------------ dial.go | 20 ++++++ transport/ray/connection.go | 96 +++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 99 deletions(-) create mode 100644 dial.go create mode 100644 transport/ray/connection.go diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index 176b94a44..f65ac70e5 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -3,12 +3,10 @@ package outbound import ( "context" "io" - "time" "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/app/proxyman/mux" - "v2ray.com/core/common/buf" "v2ray.com/core/common/errors" "v2ray.com/core/common/net" "v2ray.com/core/proxy" @@ -94,6 +92,8 @@ func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) { } } +var zeroAddr net.Addr = &net.TCPAddr{IP: []byte{0, 0, 0, 0}, Port: 0} + // Dial implements proxy.Dialer.Dial(). func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) { if h.senderSettings != nil { @@ -105,7 +105,7 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn ctx = proxy.ContextWithTarget(ctx, dest) stream := ray.NewRay(ctx) go handler.Dispatch(ctx, stream) - return NewConnection(stream), nil + return ray.NewConnection(stream, zeroAddr, zeroAddr), nil } newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog() @@ -122,99 +122,3 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn return internet.Dial(ctx, dest) } - -var ( - _ buf.Reader = (*Connection)(nil) - _ buf.Writer = (*Connection)(nil) -) - -type Connection struct { - stream ray.Ray - closed bool - localAddr net.Addr - remoteAddr net.Addr - - reader *buf.BufferedReader - writer buf.Writer -} - -func NewConnection(stream ray.Ray) *Connection { - return &Connection{ - stream: stream, - localAddr: &net.TCPAddr{ - IP: []byte{0, 0, 0, 0}, - Port: 0, - }, - remoteAddr: &net.TCPAddr{ - IP: []byte{0, 0, 0, 0}, - Port: 0, - }, - reader: buf.NewBufferedReader(stream.InboundOutput()), - writer: stream.InboundInput(), - } -} - -// Read implements net.Conn.Read(). -func (v *Connection) Read(b []byte) (int, error) { - if v.closed { - return 0, io.EOF - } - return v.reader.Read(b) -} - -func (v *Connection) ReadMultiBuffer() (buf.MultiBuffer, error) { - return v.reader.ReadMultiBuffer() -} - -// Write implements net.Conn.Write(). -func (v *Connection) Write(b []byte) (int, error) { - if v.closed { - return 0, io.ErrClosedPipe - } - - l := len(b) - mb := buf.NewMultiBufferCap(l/buf.Size + 1) - mb.Write(b) - return l, v.writer.WriteMultiBuffer(mb) -} - -func (v *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error { - if v.closed { - return io.ErrClosedPipe - } - - return v.writer.WriteMultiBuffer(mb) -} - -// Close implements net.Conn.Close(). -func (v *Connection) Close() error { - v.closed = true - v.stream.InboundInput().Close() - v.stream.InboundOutput().CloseError() - return nil -} - -// LocalAddr implements net.Conn.LocalAddr(). -func (v *Connection) LocalAddr() net.Addr { - return v.localAddr -} - -// RemoteAddr implements net.Conn.RemoteAddr(). -func (v *Connection) RemoteAddr() net.Addr { - return v.remoteAddr -} - -// SetDeadline implements net.Conn.SetDeadline(). -func (v *Connection) SetDeadline(t time.Time) error { - return nil -} - -// SetReadDeadline implements net.Conn.SetReadDeadline(). -func (v *Connection) SetReadDeadline(t time.Time) error { - return nil -} - -// SetWriteDeadline implement net.Conn.SetWriteDeadline(). -func (v *Connection) SetWriteDeadline(t time.Time) error { - return nil -} diff --git a/dial.go b/dial.go new file mode 100644 index 000000000..aef5d8f2a --- /dev/null +++ b/dial.go @@ -0,0 +1,20 @@ +package core + +import ( + "context" + + "v2ray.com/core/common/net" + "v2ray.com/core/transport/ray" +) + +// Dial provides an easy way for upstream caller to create net.Conn through V2Ray. +// It dispatches the request to the given destination by the given V2Ray instance. +// Since it is under a proxy context, the LocalAddr() and RemoteAddr() in returned net.Conn +// will not show real addresses being used for communication. +func Dial(ctx context.Context, v *Instance, dest net.Destination) (net.Conn, error) { + r, err := v.Dispatcher().Dispatch(ctx, dest) + if err != nil { + return nil, err + } + return ray.NewConnection(r, &net.TCPAddr{IP: []byte{0, 0, 0, 0}}, &net.TCPAddr{IP: []byte{0, 0, 0, 0}}), nil +} diff --git a/transport/ray/connection.go b/transport/ray/connection.go new file mode 100644 index 000000000..9ef8299cf --- /dev/null +++ b/transport/ray/connection.go @@ -0,0 +1,96 @@ +package ray + +import ( + "io" + "net" + "time" + + "v2ray.com/core/common/buf" +) + +type connection struct { + stream Ray + closed bool + localAddr net.Addr + remoteAddr net.Addr + + reader *buf.BufferedReader + writer buf.Writer +} + +// NewConnection wraps a Ray into net.Conn. +func NewConnection(stream InboundRay, localAddr net.Addr, remoteAddr net.Addr) net.Conn { + return &connection{ + stream: stream, + localAddr: localAddr, + remoteAddr: remoteAddr, + reader: buf.NewBufferedReader(stream.InboundOutput()), + writer: stream.InboundInput(), + } +} + +// Read implements net.Conn.Read(). +func (c *connection) Read(b []byte) (int, error) { + if c.closed { + return 0, io.EOF + } + return c.reader.Read(b) +} + +// ReadMultiBuffer implements buf.Reader. +func (c *connection) ReadMultiBuffer() (buf.MultiBuffer, error) { + return c.reader.ReadMultiBuffer() +} + +// Write implements net.Conn.Write(). +func (c *connection) Write(b []byte) (int, error) { + if c.closed { + return 0, io.ErrClosedPipe + } + + l := len(b) + mb := buf.NewMultiBufferCap(l/buf.Size + 1) + mb.Write(b) + return l, c.writer.WriteMultiBuffer(mb) +} + +func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { + if c.closed { + return io.ErrClosedPipe + } + + return c.writer.WriteMultiBuffer(mb) +} + +// Close implements net.Conn.Close(). +func (c *connection) Close() error { + c.closed = true + c.stream.InboundInput().Close() + c.stream.InboundOutput().CloseError() + return nil +} + +// LocalAddr implements net.Conn.LocalAddr(). +func (c *connection) LocalAddr() net.Addr { + return c.localAddr +} + +// RemoteAddr implements net.Conn.RemoteAddr(). +func (c *connection) RemoteAddr() net.Addr { + return c.remoteAddr +} + +// SetDeadline implements net.Conn.SetDeadline(). +func (c *connection) SetDeadline(t time.Time) error { + return nil +} + +// SetReadDeadline implements net.Conn.SetReadDeadline(). +func (c *connection) SetReadDeadline(t time.Time) error { + return nil +} + +// SetWriteDeadline implement net.Conn.SetWriteDeadline(). +func (c *connection) SetWriteDeadline(t time.Time) error { + return nil +} From 7fab28cf0b3b269165ba8a6d666df6b2102b928b Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 10 Jan 2018 17:33:15 +0100 Subject: [PATCH 10/63] fix build break --- transport/ray/connection.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/ray/connection.go b/transport/ray/connection.go index 9ef8299cf..4a02443ff 100644 --- a/transport/ray/connection.go +++ b/transport/ray/connection.go @@ -9,7 +9,7 @@ import ( ) type connection struct { - stream Ray + stream InboundRay closed bool localAddr net.Addr remoteAddr net.Addr From fa72ccce71c8925ce819b39a8df742a15c3dee23 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 12 Jan 2018 11:13:27 +0100 Subject: [PATCH 11/63] Update version --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index 410e9e5c7..972bebbf8 100644 --- a/core.go +++ b/core.go @@ -18,7 +18,7 @@ import ( ) var ( - version = "3.6" + version = "3.7" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." From 72e9ef8fb325b3322b78b7a74ae5122c1fd44176 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 12 Jan 2018 13:27:18 +0100 Subject: [PATCH 12/63] fix release ci --- release/release-ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/release-ci.sh b/release/release-ci.sh index ecb95333b..d84876675 100755 --- a/release/release-ci.sh +++ b/release/release-ci.sh @@ -65,7 +65,7 @@ $GOPATH/bin/vbuild --os=openbsd --arch=amd64 --zip --sign #--encrypt RELBODY="https://www.v2ray.com/chapter_00/01_versions.html" JSON_DATA=$(echo "{}" | jq -c ".tag_name=\"${RELEASE_TAG}\"") JSON_DATA=$(echo ${JSON_DATA} | jq -c ".prerelease=${PRERELEASE}") -JSON_DATA=$(echo ${JSON_DATA} | jq -c ".body=${RELBODY}") +JSON_DATA=$(echo ${JSON_DATA} | jq -c ".body=\"${RELBODY}\"") RELEASE_ID=$(curl --data "${JSON_DATA}" -H "Authorization: token ${GITHUB_TOKEN}" -X POST https://api.github.com/repos/v2ray/v2ray-core/releases | jq ".id") function upload() { From ddb6437976c33a8641bb11b022dc850d9c7d1dea Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 13 Jan 2018 00:07:29 +0100 Subject: [PATCH 13/63] remove unused code --- app/api/api.go | 4 ---- app/api/config.go | 9 --------- 2 files changed, 13 deletions(-) delete mode 100644 app/api/api.go delete mode 100644 app/api/config.go diff --git a/app/api/api.go b/app/api/api.go deleted file mode 100644 index 21e278905..000000000 --- a/app/api/api.go +++ /dev/null @@ -1,4 +0,0 @@ -package api - -type ApiServer struct { -} diff --git a/app/api/config.go b/app/api/config.go deleted file mode 100644 index 31abfb093..000000000 --- a/app/api/config.go +++ /dev/null @@ -1,9 +0,0 @@ -package api - -import ( - "v2ray.com/core/common/net" -) - -type Config struct { - DirectPort net.Port -} From b5caea67ac8e53df8e1d4d2a4fa6139a0002e6a6 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 13 Jan 2018 00:07:37 +0100 Subject: [PATCH 14/63] gofmt --- app/dispatcher/errors.generated.go | 4 +++- app/dns/server_test.go | 4 ++-- app/proxyman/errors.generated.go | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/dispatcher/errors.generated.go b/app/dispatcher/errors.generated.go index 204f6d572..424bd9812 100644 --- a/app/dispatcher/errors.generated.go +++ b/app/dispatcher/errors.generated.go @@ -2,4 +2,6 @@ package dispatcher import "v2ray.com/core/common/errors" -func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Dispatcher") } +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).Path("App", "Dispatcher") +} diff --git a/app/dns/server_test.go b/app/dns/server_test.go index 5b0a21805..5fd1bc929 100644 --- a/app/dns/server_test.go +++ b/app/dns/server_test.go @@ -6,8 +6,8 @@ import ( "v2ray.com/core" "v2ray.com/core/app/dispatcher" . "v2ray.com/core/app/dns" - "v2ray.com/core/app/proxyman" "v2ray.com/core/app/policy" + "v2ray.com/core/app/proxyman" _ "v2ray.com/core/app/proxyman/outbound" "v2ray.com/core/common/net" "v2ray.com/core/common/serial" @@ -70,7 +70,7 @@ func TestUDPServer(t *testing.T) { serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ - &core.OutboundHandlerConfig{ + { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, diff --git a/app/proxyman/errors.generated.go b/app/proxyman/errors.generated.go index 61b329269..b69ca4bc4 100644 --- a/app/proxyman/errors.generated.go +++ b/app/proxyman/errors.generated.go @@ -2,4 +2,6 @@ package proxyman import "v2ray.com/core/common/errors" -func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Proxyman") } +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).Path("App", "Proxyman") +} From a6c0ef11ba2026e9f8bfad618ebc9c7e8287ffa1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 17 Jan 2018 16:18:38 +0100 Subject: [PATCH 15/63] check connection state for every write operation --- transport/internet/kcp/connection.go | 49 +++++++++++++++++++--------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/transport/internet/kcp/connection.go b/transport/internet/kcp/connection.go index d7e69f9d2..b7b0652f5 100644 --- a/transport/internet/kcp/connection.go +++ b/transport/internet/kcp/connection.go @@ -341,21 +341,30 @@ func (c *Connection) Write(b []byte) (int, error) { totalWritten := 0 for { - if c == nil || c.State() != StateActive { - return totalWritten, io.ErrClosedPipe - } + dataWritten := false + for { + if c == nil || c.State() != StateActive { + return totalWritten, io.ErrClosedPipe + } + if !c.sendingWorker.Push(func(bb []byte) (int, error) { + n := copy(bb[:c.mss], b[totalWritten:]) + totalWritten += n + return n, nil + }) { + break + } + + dataWritten = true - for c.sendingWorker.Push(func(bb []byte) (int, error) { - n := copy(bb[:c.mss], b[totalWritten:]) - totalWritten += n - return n, nil - }) { - c.dataUpdater.WakeUp() if totalWritten == len(b) { return totalWritten, nil } } + if dataWritten { + c.dataUpdater.WakeUp() + } + if err := c.waitForDataOutput(); err != nil { return totalWritten, err } @@ -367,19 +376,27 @@ func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error { defer mb.Release() for { - if c == nil || c.State() != StateActive { - return io.ErrClosedPipe - } + dataWritten := false + for { + if c == nil || c.State() != StateActive { + return io.ErrClosedPipe + } - for c.sendingWorker.Push(func(bb []byte) (int, error) { - return mb.Read(bb[:c.mss]) - }) { - c.dataUpdater.WakeUp() + if !c.sendingWorker.Push(func(bb []byte) (int, error) { + return mb.Read(bb[:c.mss]) + }) { + break + } + dataWritten = true if mb.IsEmpty() { return nil } } + if dataWritten { + c.dataUpdater.WakeUp() + } + if err := c.waitForDataOutput(); err != nil { return err } From 630a76d06ac3df001c3064b7d70b0eaca6dd07a8 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 17 Jan 2018 17:36:14 +0100 Subject: [PATCH 16/63] kcp cleanup --- transport/internet/kcp/connection.go | 4 +- transport/internet/kcp/listener.go | 74 ++++++------- transport/internet/kcp/receiving.go | 98 ++++++++-------- transport/internet/kcp/segment.go | 135 +++++++++++----------- transport/internet/kcp/segment_test.go | 14 +-- transport/internet/kcp/sending.go | 148 ++++++++++++------------- 6 files changed, 236 insertions(+), 237 deletions(-) diff --git a/transport/internet/kcp/connection.go b/transport/internet/kcp/connection.go index b7b0652f5..eac7a6f43 100644 --- a/transport/internet/kcp/connection.go +++ b/transport/internet/kcp/connection.go @@ -572,7 +572,7 @@ func (c *Connection) Input(segments []Segment) { c.dataInput.Signal() c.dataOutput.Signal() } - c.sendingWorker.ProcessReceivingNext(seg.ReceivinNext) + c.sendingWorker.ProcessReceivingNext(seg.ReceivingNext) c.receivingWorker.ProcessSendingNext(seg.SendingNext) c.roundTrip.UpdatePeerRTO(seg.PeerRTO, current) seg.Release() @@ -628,7 +628,7 @@ func (c *Connection) Ping(current uint32, cmd Command) { seg := NewCmdOnlySegment() seg.Conv = c.meta.Conversation seg.Cmd = cmd - seg.ReceivinNext = c.receivingWorker.NextNumber() + seg.ReceivingNext = c.receivingWorker.NextNumber() seg.SendingNext = c.sendingWorker.FirstUnacknowledged() seg.PeerRTO = c.roundTrip.Timeout() if c.State() == StateReadyToClose { diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index 993f2fced..c46de60ca 100644 --- a/transport/internet/kcp/listener.go +++ b/transport/internet/kcp/listener.go @@ -74,25 +74,25 @@ func NewListener(ctx context.Context, address net.Address, port net.Port, addCon return l, nil } -func (v *Listener) OnReceive(payload *buf.Buffer, src net.Destination, originalDest net.Destination) { +func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination, originalDest net.Destination) { defer payload.Release() - segments := v.reader.Read(payload.Bytes()) + segments := l.reader.Read(payload.Bytes()) if len(segments) == 0 { newError("discarding invalid payload from ", src).WriteToLog() return } - v.Lock() - defer v.Unlock() + l.Lock() + defer l.Unlock() select { - case <-v.ctx.Done(): + case <-l.ctx.Done(): return default: } - if v.hub == nil { + if l.hub == nil { return } @@ -104,7 +104,7 @@ func (v *Listener) OnReceive(payload *buf.Buffer, src net.Destination, originalD Port: src.Port, Conv: conv, } - conn, found := v.sessions[id] + conn, found := l.sessions[id] if !found { if cmd == CommandTerminate { @@ -112,73 +112,73 @@ func (v *Listener) OnReceive(payload *buf.Buffer, src net.Destination, originalD } writer := &Writer{ id: id, - hub: v.hub, + hub: l.hub, dest: src, - listener: v, + listener: l, } remoteAddr := &net.UDPAddr{ IP: src.Address.IP(), Port: int(src.Port), } - localAddr := v.hub.Addr() + localAddr := l.hub.Addr() conn = NewConnection(ConnMetadata{ LocalAddr: localAddr, RemoteAddr: remoteAddr, Conversation: conv, }, &KCPPacketWriter{ - Header: v.header, - Security: v.security, + Header: l.header, + Security: l.security, Writer: writer, - }, writer, v.config) + }, writer, l.config) var netConn internet.Connection = conn - if v.tlsConfig != nil { - tlsConn := tls.Server(conn, v.tlsConfig) + if l.tlsConfig != nil { + tlsConn := tls.Server(conn, l.tlsConfig) netConn = tlsConn } - if !v.addConn(context.Background(), netConn) { + if !l.addConn(context.Background(), netConn) { return } - v.sessions[id] = conn + l.sessions[id] = conn } conn.Input(segments) } -func (v *Listener) Remove(id ConnectionID) { +func (l *Listener) Remove(id ConnectionID) { select { - case <-v.ctx.Done(): + case <-l.ctx.Done(): return default: - v.Lock() - delete(v.sessions, id) - v.Unlock() + l.Lock() + delete(l.sessions, id) + l.Unlock() } } // Close stops listening on the UDP address. Already Accepted connections are not closed. -func (v *Listener) Close() error { - v.hub.Close() +func (l *Listener) Close() error { + l.hub.Close() - v.Lock() - defer v.Unlock() + l.Lock() + defer l.Unlock() - for _, conn := range v.sessions { + for _, conn := range l.sessions { go conn.Terminate() } return nil } -func (v *Listener) ActiveConnections() int { - v.Lock() - defer v.Unlock() +func (l *Listener) ActiveConnections() int { + l.Lock() + defer l.Unlock() - return len(v.sessions) + return len(l.sessions) } // Addr returns the listener's network address, The Addr returned is shared by all invocations of Addr, so do not modify it. -func (v *Listener) Addr() net.Addr { - return v.hub.Addr() +func (l *Listener) Addr() net.Addr { + return l.hub.Addr() } type Writer struct { @@ -188,12 +188,12 @@ type Writer struct { listener *Listener } -func (v *Writer) Write(payload []byte) (int, error) { - return v.hub.WriteTo(payload, v.dest) +func (w *Writer) Write(payload []byte) (int, error) { + return w.hub.WriteTo(payload, w.dest) } -func (v *Writer) Close() error { - v.listener.Remove(v.id) +func (w *Writer) Close() error { + w.listener.Remove(w.id) return nil } diff --git a/transport/internet/kcp/receiving.go b/transport/internet/kcp/receiving.go index 65e3afc53..5789ae077 100644 --- a/transport/internet/kcp/receiving.go +++ b/transport/internet/kcp/receiving.go @@ -20,42 +20,42 @@ func NewReceivingWindow(size uint32) *ReceivingWindow { } } -func (v *ReceivingWindow) Size() uint32 { - return v.size +func (w *ReceivingWindow) Size() uint32 { + return w.size } -func (v *ReceivingWindow) Position(idx uint32) uint32 { - return (idx + v.start) % v.size +func (w *ReceivingWindow) Position(idx uint32) uint32 { + return (idx + w.start) % w.size } -func (v *ReceivingWindow) Set(idx uint32, value *DataSegment) bool { - pos := v.Position(idx) - if v.list[pos] != nil { +func (w *ReceivingWindow) Set(idx uint32, value *DataSegment) bool { + pos := w.Position(idx) + if w.list[pos] != nil { return false } - v.list[pos] = value + w.list[pos] = value return true } -func (v *ReceivingWindow) Remove(idx uint32) *DataSegment { - pos := v.Position(idx) - e := v.list[pos] - v.list[pos] = nil +func (w *ReceivingWindow) Remove(idx uint32) *DataSegment { + pos := w.Position(idx) + e := w.list[pos] + w.list[pos] = nil return e } -func (v *ReceivingWindow) RemoveFirst() *DataSegment { - return v.Remove(0) +func (w *ReceivingWindow) RemoveFirst() *DataSegment { + return w.Remove(0) } func (w *ReceivingWindow) HasFirst() bool { return w.list[w.Position(0)] != nil } -func (v *ReceivingWindow) Advance() { - v.start++ - if v.start == v.size { - v.start = 0 +func (w *ReceivingWindow) Advance() { + w.start++ + if w.start == w.size { + w.start = 0 } } @@ -79,70 +79,70 @@ func NewAckList(writer SegmentWriter) *AckList { } } -func (v *AckList) Add(number uint32, timestamp uint32) { - v.timestamps = append(v.timestamps, timestamp) - v.numbers = append(v.numbers, number) - v.nextFlush = append(v.nextFlush, 0) - v.dirty = true +func (l *AckList) Add(number uint32, timestamp uint32) { + l.timestamps = append(l.timestamps, timestamp) + l.numbers = append(l.numbers, number) + l.nextFlush = append(l.nextFlush, 0) + l.dirty = true } -func (v *AckList) Clear(una uint32) { +func (l *AckList) Clear(una uint32) { count := 0 - for i := 0; i < len(v.numbers); i++ { - if v.numbers[i] < una { + for i := 0; i < len(l.numbers); i++ { + if l.numbers[i] < una { continue } if i != count { - v.numbers[count] = v.numbers[i] - v.timestamps[count] = v.timestamps[i] - v.nextFlush[count] = v.nextFlush[i] + l.numbers[count] = l.numbers[i] + l.timestamps[count] = l.timestamps[i] + l.nextFlush[count] = l.nextFlush[i] } count++ } - if count < len(v.numbers) { - v.numbers = v.numbers[:count] - v.timestamps = v.timestamps[:count] - v.nextFlush = v.nextFlush[:count] - v.dirty = true + if count < len(l.numbers) { + l.numbers = l.numbers[:count] + l.timestamps = l.timestamps[:count] + l.nextFlush = l.nextFlush[:count] + l.dirty = true } } -func (v *AckList) Flush(current uint32, rto uint32) { - v.flushCandidates = v.flushCandidates[:0] +func (l *AckList) Flush(current uint32, rto uint32) { + l.flushCandidates = l.flushCandidates[:0] seg := NewAckSegment() - for i := 0; i < len(v.numbers); i++ { - if v.nextFlush[i] > current { - if len(v.flushCandidates) < cap(v.flushCandidates) { - v.flushCandidates = append(v.flushCandidates, v.numbers[i]) + for i := 0; i < len(l.numbers); i++ { + if l.nextFlush[i] > current { + if len(l.flushCandidates) < cap(l.flushCandidates) { + l.flushCandidates = append(l.flushCandidates, l.numbers[i]) } continue } - seg.PutNumber(v.numbers[i]) - seg.PutTimestamp(v.timestamps[i]) + seg.PutNumber(l.numbers[i]) + seg.PutTimestamp(l.timestamps[i]) timeout := rto / 2 if timeout < 20 { timeout = 20 } - v.nextFlush[i] = current + timeout + l.nextFlush[i] = current + timeout if seg.IsFull() { - v.writer.Write(seg) + l.writer.Write(seg) seg.Release() seg = NewAckSegment() - v.dirty = false + l.dirty = false } } - if v.dirty || !seg.IsEmpty() { - for _, number := range v.flushCandidates { + if l.dirty || !seg.IsEmpty() { + for _, number := range l.flushCandidates { if seg.IsFull() { break } seg.PutNumber(number) } - v.writer.Write(seg) + l.writer.Write(seg) seg.Release() - v.dirty = false + l.dirty = false } } diff --git a/transport/internet/kcp/segment.go b/transport/internet/kcp/segment.go index 7885c4726..5acf25be1 100644 --- a/transport/internet/kcp/segment.go +++ b/transport/internet/kcp/segment.go @@ -53,47 +53,47 @@ func NewDataSegment() *DataSegment { return new(DataSegment) } -func (v *DataSegment) Conversation() uint16 { - return v.Conv +func (s *DataSegment) Conversation() uint16 { + return s.Conv } -func (v *DataSegment) Command() Command { +func (*DataSegment) Command() Command { return CommandData } -func (v *DataSegment) Detach() *buf.Buffer { - r := v.payload - v.payload = nil +func (s *DataSegment) Detach() *buf.Buffer { + r := s.payload + s.payload = nil return r } -func (v *DataSegment) Data() *buf.Buffer { - if v.payload == nil { - v.payload = buf.New() +func (s *DataSegment) Data() *buf.Buffer { + if s.payload == nil { + s.payload = buf.New() } - return v.payload + return s.payload } -func (v *DataSegment) Bytes() buf.Supplier { +func (s *DataSegment) Bytes() buf.Supplier { return func(b []byte) (int, error) { - b = serial.Uint16ToBytes(v.Conv, b[:0]) - b = append(b, byte(CommandData), byte(v.Option)) - b = serial.Uint32ToBytes(v.Timestamp, b) - b = serial.Uint32ToBytes(v.Number, b) - b = serial.Uint32ToBytes(v.SendingNext, b) - b = serial.Uint16ToBytes(uint16(v.payload.Len()), b) - b = append(b, v.payload.Bytes()...) + b = serial.Uint16ToBytes(s.Conv, b[:0]) + b = append(b, byte(CommandData), byte(s.Option)) + b = serial.Uint32ToBytes(s.Timestamp, b) + b = serial.Uint32ToBytes(s.Number, b) + b = serial.Uint32ToBytes(s.SendingNext, b) + b = serial.Uint16ToBytes(uint16(s.payload.Len()), b) + b = append(b, s.payload.Bytes()...) return len(b), nil } } -func (v *DataSegment) ByteSize() int { - return 2 + 1 + 1 + 4 + 4 + 4 + 2 + v.payload.Len() +func (s *DataSegment) ByteSize() int { + return 2 + 1 + 1 + 4 + 4 + 4 + 2 + s.payload.Len() } -func (v *DataSegment) Release() { - v.payload.Release() - v.payload = nil +func (s *DataSegment) Release() { + s.payload.Release() + s.payload = nil } type AckSegment struct { @@ -113,94 +113,93 @@ func NewAckSegment() *AckSegment { } } -func (v *AckSegment) Conversation() uint16 { - return v.Conv +func (s *AckSegment) Conversation() uint16 { + return s.Conv } -func (v *AckSegment) Command() Command { +func (*AckSegment) Command() Command { return CommandACK } -func (v *AckSegment) PutTimestamp(timestamp uint32) { - if timestamp-v.Timestamp < 0x7FFFFFFF { - v.Timestamp = timestamp +func (s *AckSegment) PutTimestamp(timestamp uint32) { + if timestamp-s.Timestamp < 0x7FFFFFFF { + s.Timestamp = timestamp } } -func (v *AckSegment) PutNumber(number uint32) { - v.NumberList = append(v.NumberList, number) +func (s *AckSegment) PutNumber(number uint32) { + s.NumberList = append(s.NumberList, number) } -func (v *AckSegment) IsFull() bool { - return len(v.NumberList) == ackNumberLimit +func (s *AckSegment) IsFull() bool { + return len(s.NumberList) == ackNumberLimit } -func (v *AckSegment) IsEmpty() bool { - return len(v.NumberList) == 0 +func (s *AckSegment) IsEmpty() bool { + return len(s.NumberList) == 0 } -func (v *AckSegment) ByteSize() int { - return 2 + 1 + 1 + 4 + 4 + 4 + 1 + len(v.NumberList)*4 +func (s *AckSegment) ByteSize() int { + return 2 + 1 + 1 + 4 + 4 + 4 + 1 + len(s.NumberList)*4 } -func (v *AckSegment) Bytes() buf.Supplier { +func (s *AckSegment) Bytes() buf.Supplier { return func(b []byte) (int, error) { - b = serial.Uint16ToBytes(v.Conv, b[:0]) - b = append(b, byte(CommandACK), byte(v.Option)) - b = serial.Uint32ToBytes(v.ReceivingWindow, b) - b = serial.Uint32ToBytes(v.ReceivingNext, b) - b = serial.Uint32ToBytes(v.Timestamp, b) - count := byte(len(v.NumberList)) + b = serial.Uint16ToBytes(s.Conv, b[:0]) + b = append(b, byte(CommandACK), byte(s.Option)) + b = serial.Uint32ToBytes(s.ReceivingWindow, b) + b = serial.Uint32ToBytes(s.ReceivingNext, b) + b = serial.Uint32ToBytes(s.Timestamp, b) + count := byte(len(s.NumberList)) b = append(b, count) - for _, number := range v.NumberList { + for _, number := range s.NumberList { b = serial.Uint32ToBytes(number, b) } - return v.ByteSize(), nil + return s.ByteSize(), nil } } -func (v *AckSegment) Release() { - v.NumberList = nil +func (s *AckSegment) Release() { + s.NumberList = nil } type CmdOnlySegment struct { - Conv uint16 - Cmd Command - Option SegmentOption - SendingNext uint32 - ReceivinNext uint32 - PeerRTO uint32 + Conv uint16 + Cmd Command + Option SegmentOption + SendingNext uint32 + ReceivingNext uint32 + PeerRTO uint32 } func NewCmdOnlySegment() *CmdOnlySegment { return new(CmdOnlySegment) } -func (v *CmdOnlySegment) Conversation() uint16 { - return v.Conv +func (s *CmdOnlySegment) Conversation() uint16 { + return s.Conv } -func (v *CmdOnlySegment) Command() Command { - return v.Cmd +func (s *CmdOnlySegment) Command() Command { + return s.Cmd } -func (v *CmdOnlySegment) ByteSize() int { +func (*CmdOnlySegment) ByteSize() int { return 2 + 1 + 1 + 4 + 4 + 4 } -func (v *CmdOnlySegment) Bytes() buf.Supplier { +func (s *CmdOnlySegment) Bytes() buf.Supplier { return func(b []byte) (int, error) { - b = serial.Uint16ToBytes(v.Conv, b[:0]) - b = append(b, byte(v.Cmd), byte(v.Option)) - b = serial.Uint32ToBytes(v.SendingNext, b) - b = serial.Uint32ToBytes(v.ReceivinNext, b) - b = serial.Uint32ToBytes(v.PeerRTO, b) + b = serial.Uint16ToBytes(s.Conv, b[:0]) + b = append(b, byte(s.Cmd), byte(s.Option)) + b = serial.Uint32ToBytes(s.SendingNext, b) + b = serial.Uint32ToBytes(s.ReceivingNext, b) + b = serial.Uint32ToBytes(s.PeerRTO, b) return len(b), nil } } -func (v *CmdOnlySegment) Release() { -} +func (*CmdOnlySegment) Release() {} func ReadSegment(buf []byte) (Segment, []byte) { if len(buf) < 4 { @@ -286,7 +285,7 @@ func ReadSegment(buf []byte) (Segment, []byte) { seg.SendingNext = serial.BytesToUint32(buf) buf = buf[4:] - seg.ReceivinNext = serial.BytesToUint32(buf) + seg.ReceivingNext = serial.BytesToUint32(buf) buf = buf[4:] seg.PeerRTO = serial.BytesToUint32(buf) diff --git a/transport/internet/kcp/segment_test.go b/transport/internet/kcp/segment_test.go index 57790f166..f12d488e1 100644 --- a/transport/internet/kcp/segment_test.go +++ b/transport/internet/kcp/segment_test.go @@ -100,12 +100,12 @@ func TestCmdSegment(t *testing.T) { assert := With(t) seg := &CmdOnlySegment{ - Conv: 1, - Cmd: CommandPing, - Option: SegmentOptionClose, - SendingNext: 11, - ReceivinNext: 13, - PeerRTO: 15, + Conv: 1, + Cmd: CommandPing, + Option: SegmentOptionClose, + SendingNext: 11, + ReceivingNext: 13, + PeerRTO: 15, } nBytes := seg.ByteSize() @@ -120,6 +120,6 @@ func TestCmdSegment(t *testing.T) { assert(byte(seg2.Command()), Equals, byte(seg.Command())) assert(byte(seg2.Option), Equals, byte(seg.Option)) assert(seg2.SendingNext, Equals, seg.SendingNext) - assert(seg2.ReceivinNext, Equals, seg.ReceivinNext) + assert(seg2.ReceivingNext, Equals, seg.ReceivingNext) assert(seg2.PeerRTO, Equals, seg.PeerRTO) } diff --git a/transport/internet/kcp/sending.go b/transport/internet/kcp/sending.go index 7b2d9769d..3fcf3ceb7 100644 --- a/transport/internet/kcp/sending.go +++ b/transport/internet/kcp/sending.go @@ -209,59 +209,59 @@ func NewSendingWorker(kcp *Connection) *SendingWorker { return worker } -func (v *SendingWorker) Release() { - v.Lock() - v.window.Release() - v.Unlock() +func (w *SendingWorker) Release() { + w.Lock() + w.window.Release() + w.Unlock() } -func (v *SendingWorker) ProcessReceivingNext(nextNumber uint32) { - v.Lock() - defer v.Unlock() +func (w *SendingWorker) ProcessReceivingNext(nextNumber uint32) { + w.Lock() + defer w.Unlock() - v.ProcessReceivingNextWithoutLock(nextNumber) + w.ProcessReceivingNextWithoutLock(nextNumber) } -func (v *SendingWorker) ProcessReceivingNextWithoutLock(nextNumber uint32) { - v.window.Clear(nextNumber) - v.FindFirstUnacknowledged() +func (w *SendingWorker) ProcessReceivingNextWithoutLock(nextNumber uint32) { + w.window.Clear(nextNumber) + w.FindFirstUnacknowledged() } -func (v *SendingWorker) FindFirstUnacknowledged() { - first := v.firstUnacknowledged - if !v.window.IsEmpty() { - v.firstUnacknowledged = v.window.FirstNumber() +func (w *SendingWorker) FindFirstUnacknowledged() { + first := w.firstUnacknowledged + if !w.window.IsEmpty() { + w.firstUnacknowledged = w.window.FirstNumber() } else { - v.firstUnacknowledged = v.nextNumber + w.firstUnacknowledged = w.nextNumber } - if first != v.firstUnacknowledged { - v.firstUnacknowledgedUpdated = true + if first != w.firstUnacknowledged { + w.firstUnacknowledgedUpdated = true } } -func (v *SendingWorker) processAck(number uint32) bool { +func (w *SendingWorker) processAck(number uint32) bool { // number < v.firstUnacknowledged || number >= v.nextNumber - if number-v.firstUnacknowledged > 0x7FFFFFFF || number-v.nextNumber < 0x7FFFFFFF { + if number-w.firstUnacknowledged > 0x7FFFFFFF || number-w.nextNumber < 0x7FFFFFFF { return false } - removed := v.window.Remove(number - v.firstUnacknowledged) + removed := w.window.Remove(number - w.firstUnacknowledged) if removed { - v.FindFirstUnacknowledged() + w.FindFirstUnacknowledged() } return removed } -func (v *SendingWorker) ProcessSegment(current uint32, seg *AckSegment, rto uint32) { +func (w *SendingWorker) ProcessSegment(current uint32, seg *AckSegment, rto uint32) { defer seg.Release() - v.Lock() - defer v.Unlock() + w.Lock() + defer w.Unlock() - if v.remoteNextNumber < seg.ReceivingWindow { - v.remoteNextNumber = seg.ReceivingWindow + if w.remoteNextNumber < seg.ReceivingWindow { + w.remoteNextNumber = seg.ReceivingWindow } - v.ProcessReceivingNextWithoutLock(seg.ReceivingNext) + w.ProcessReceivingNextWithoutLock(seg.ReceivingNext) if seg.IsEmpty() { return @@ -270,7 +270,7 @@ func (v *SendingWorker) ProcessSegment(current uint32, seg *AckSegment, rto uint var maxack uint32 var maxackRemoved bool for _, number := range seg.NumberList { - removed := v.processAck(number) + removed := w.processAck(number) if maxack < number { maxack = number maxackRemoved = removed @@ -278,100 +278,100 @@ func (v *SendingWorker) ProcessSegment(current uint32, seg *AckSegment, rto uint } if maxackRemoved { - v.window.HandleFastAck(maxack, rto) + w.window.HandleFastAck(maxack, rto) if current-seg.Timestamp < 10000 { - v.conn.roundTrip.Update(current-seg.Timestamp, current) + w.conn.roundTrip.Update(current-seg.Timestamp, current) } } } -func (v *SendingWorker) Push(f buf.Supplier) bool { - v.Lock() - defer v.Unlock() +func (w *SendingWorker) Push(f buf.Supplier) bool { + w.Lock() + defer w.Unlock() - if v.window.IsFull() { + if w.window.IsFull() { return false } - b := v.window.Push(v.nextNumber) - v.nextNumber++ + b := w.window.Push(w.nextNumber) + w.nextNumber++ common.Must(b.Reset(f)) return true } -func (v *SendingWorker) Write(seg Segment) error { +func (w *SendingWorker) Write(seg Segment) error { dataSeg := seg.(*DataSegment) - dataSeg.Conv = v.conn.meta.Conversation - dataSeg.SendingNext = v.firstUnacknowledged + dataSeg.Conv = w.conn.meta.Conversation + dataSeg.SendingNext = w.firstUnacknowledged dataSeg.Option = 0 - if v.conn.State() == StateReadyToClose { + if w.conn.State() == StateReadyToClose { dataSeg.Option = SegmentOptionClose } - return v.conn.output.Write(dataSeg) + return w.conn.output.Write(dataSeg) } -func (v *SendingWorker) OnPacketLoss(lossRate uint32) { - if !v.conn.Config.Congestion || v.conn.roundTrip.Timeout() == 0 { +func (w *SendingWorker) OnPacketLoss(lossRate uint32) { + if !w.conn.Config.Congestion || w.conn.roundTrip.Timeout() == 0 { return } if lossRate >= 15 { - v.controlWindow = 3 * v.controlWindow / 4 + w.controlWindow = 3 * w.controlWindow / 4 } else if lossRate <= 5 { - v.controlWindow += v.controlWindow / 4 + w.controlWindow += w.controlWindow / 4 } - if v.controlWindow < 16 { - v.controlWindow = 16 + if w.controlWindow < 16 { + w.controlWindow = 16 } - if v.controlWindow > 2*v.conn.Config.GetSendingInFlightSize() { - v.controlWindow = 2 * v.conn.Config.GetSendingInFlightSize() + if w.controlWindow > 2*w.conn.Config.GetSendingInFlightSize() { + w.controlWindow = 2 * w.conn.Config.GetSendingInFlightSize() } } -func (v *SendingWorker) Flush(current uint32) { - v.Lock() +func (w *SendingWorker) Flush(current uint32) { + w.Lock() - cwnd := v.firstUnacknowledged + v.conn.Config.GetSendingInFlightSize() - if cwnd > v.remoteNextNumber { - cwnd = v.remoteNextNumber + cwnd := w.firstUnacknowledged + w.conn.Config.GetSendingInFlightSize() + if cwnd > w.remoteNextNumber { + cwnd = w.remoteNextNumber } - if v.conn.Config.Congestion && cwnd > v.firstUnacknowledged+v.controlWindow { - cwnd = v.firstUnacknowledged + v.controlWindow + if w.conn.Config.Congestion && cwnd > w.firstUnacknowledged+w.controlWindow { + cwnd = w.firstUnacknowledged + w.controlWindow } - if !v.window.IsEmpty() { - v.window.Flush(current, v.conn.roundTrip.Timeout(), cwnd) - v.firstUnacknowledgedUpdated = false + if !w.window.IsEmpty() { + w.window.Flush(current, w.conn.roundTrip.Timeout(), cwnd) + w.firstUnacknowledgedUpdated = false } - updated := v.firstUnacknowledgedUpdated - v.firstUnacknowledgedUpdated = false + updated := w.firstUnacknowledgedUpdated + w.firstUnacknowledgedUpdated = false - v.Unlock() + w.Unlock() if updated { - v.conn.Ping(current, CommandPing) + w.conn.Ping(current, CommandPing) } } -func (v *SendingWorker) CloseWrite() { - v.Lock() - defer v.Unlock() +func (w *SendingWorker) CloseWrite() { + w.Lock() + defer w.Unlock() - v.window.Clear(0xFFFFFFFF) + w.window.Clear(0xFFFFFFFF) } -func (v *SendingWorker) IsEmpty() bool { - v.RLock() - defer v.RUnlock() +func (w *SendingWorker) IsEmpty() bool { + w.RLock() + defer w.RUnlock() - return v.window.IsEmpty() + return w.window.IsEmpty() } -func (v *SendingWorker) UpdateNecessary() bool { - return !v.IsEmpty() +func (w *SendingWorker) UpdateNecessary() bool { + return !w.IsEmpty() } func (w *SendingWorker) FirstUnacknowledged() uint32 { From fbfbbb88413743bde34a17832fc2beb6682a714e Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 17 Jan 2018 19:09:32 +0100 Subject: [PATCH 17/63] id of v2ray instance --- v2ray.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/v2ray.go b/v2ray.go index dca5cf9a8..7e95eb413 100644 --- a/v2ray.go +++ b/v2ray.go @@ -4,6 +4,7 @@ import ( "context" "v2ray.com/core/common" + "v2ray.com/core/common/uuid" ) // Server is an instance of V2Ray. At any time, there must be at most one Server instance running. @@ -28,13 +29,16 @@ type Instance struct { ohm syncOutboundHandlerManager features []Feature + id uuid.UUID } // New returns a new V2Ray instance based on given configuration. // The instance is not started at this point. // To make sure V2Ray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional. func New(config *Config) (*Instance, error) { - var server = new(Instance) + var server = &Instance{ + id: *(uuid.New()), + } if err := config.Transport.Apply(); err != nil { return nil, err @@ -83,6 +87,11 @@ func New(config *Config) (*Instance, error) { return server, nil } +// ID returns an unique ID for this V2Ray instance. +func (s *Instance) ID() uuid.UUID { + return s.id +} + // Close shutdown the V2Ray instance. func (s *Instance) Close() { for _, f := range s.features { From 14176a340d581b7d52eed945295864638f7e79db Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 18 Jan 2018 11:35:04 +0100 Subject: [PATCH 18/63] fix error usage --- proxy/vmess/account.go | 3 +-- proxy/vmess/inbound/inbound.go | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/proxy/vmess/account.go b/proxy/vmess/account.go index 44c2908dc..57cc1c5a3 100644 --- a/proxy/vmess/account.go +++ b/proxy/vmess/account.go @@ -31,8 +31,7 @@ func (a *InternalAccount) Equals(account protocol.Account) bool { func (a *Account) AsAccount() (protocol.Account, error) { id, err := uuid.ParseString(a.Id) if err != nil { - newError("failed to parse ID").Base(err).AtError().WriteToLog() - return nil, err + return nil, newError("failed to parse ID").Base(err).AtError() } protoID := protocol.NewID(id) return &InternalAccount{ diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 5021fd5b8..df30ae4f9 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -185,7 +185,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i Status: log.AccessRejected, Reason: err, }) - newError("invalid request from ", connection.RemoteAddr(), ": ", err).AtInfo().WriteToLog() + err = newError("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo() } return err } @@ -252,7 +252,7 @@ func (h *Handler) generateCommand(ctx context.Context, request *protocol.Request if h.inboundHandlerManager != nil { handler, err := h.inboundHandlerManager.GetHandler(ctx, tag) if err != nil { - newError("failed to get detour handler: ", tag, err).AtWarning().WriteToLog() + newError("failed to get detour handler: ", tag).Base(err).AtWarning().WriteToLog() return nil } proxyHandler, port, availableMin := handler.GetRandomInboundProxy() From 0e01e28278958ecb9f800bf34e58400dec3decd8 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 18 Jan 2018 23:25:48 +0100 Subject: [PATCH 19/63] use uuid as struct --- common/protocol/headers.go | 2 +- common/protocol/id.go | 8 ++++---- common/uuid/uuid.go | 26 ++++++++++++++------------ common/uuid/uuid_test.go | 6 ++++-- proxy/vmess/encoding/encoding_test.go | 3 ++- proxy/vmess/inbound/inbound.go | 3 ++- v2ray.go | 2 +- v2ray_test.go | 3 ++- 8 files changed, 30 insertions(+), 23 deletions(-) diff --git a/common/protocol/headers.go b/common/protocol/headers.go index 1304a7e4d..092de5eaa 100644 --- a/common/protocol/headers.go +++ b/common/protocol/headers.go @@ -79,7 +79,7 @@ type ResponseHeader struct { type CommandSwitchAccount struct { Host net.Address Port net.Port - ID *uuid.UUID + ID uuid.UUID Level uint32 AlterIds uint16 ValidMin byte diff --git a/common/protocol/id.go b/common/protocol/id.go index 4360a563d..e6b0176ee 100644 --- a/common/protocol/id.go +++ b/common/protocol/id.go @@ -21,13 +21,13 @@ func DefaultIDHash(key []byte) hash.Hash { // The ID of en entity, in the form of an UUID. type ID struct { - uuid *uuid.UUID + uuid uuid.UUID cmdKey [IDBytesLen]byte } // Equals returns true if this ID equals to the other one. func (id *ID) Equals(another *ID) bool { - return id.uuid.Equals(another.uuid) + return id.uuid.Equals(&(another.uuid)) } func (id *ID) Bytes() []byte { @@ -38,7 +38,7 @@ func (id *ID) String() string { return id.uuid.String() } -func (id *ID) UUID() *uuid.UUID { +func (id *ID) UUID() uuid.UUID { return id.uuid } @@ -47,7 +47,7 @@ func (id ID) CmdKey() []byte { } // NewID returns an ID with given UUID. -func NewID(uuid *uuid.UUID) *ID { +func NewID(uuid uuid.UUID) *ID { id := &ID{uuid: uuid} md5hash := md5.New() common.Must2(md5hash.Write(uuid.Bytes())) diff --git a/common/uuid/uuid.go b/common/uuid/uuid.go index 08225b5d8..0e0ca3869 100644 --- a/common/uuid/uuid.go +++ b/common/uuid/uuid.go @@ -6,6 +6,7 @@ import ( "crypto/rand" "encoding/hex" + "v2ray.com/core/common" "v2ray.com/core/common/errors" ) @@ -46,11 +47,11 @@ func (u *UUID) Equals(another *UUID) bool { } // Next generates a deterministic random UUID based on this UUID. -func (u *UUID) Next() *UUID { +func (u *UUID) Next() UUID { md5hash := md5.New() md5hash.Write(u.Bytes()) md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81")) - newid := new(UUID) + var newid UUID for { md5hash.Sum(newid[:0]) if !newid.Equals(u) { @@ -61,30 +62,31 @@ func (u *UUID) Next() *UUID { } // New creates an UUID with random value. -func New() *UUID { - uuid := new(UUID) - rand.Read(uuid.Bytes()) +func New() UUID { + var uuid UUID + common.Must2(rand.Read(uuid.Bytes())) return uuid } // ParseBytes converts an UUID in byte form to object. -func ParseBytes(b []byte) (*UUID, error) { +func ParseBytes(b []byte) (UUID, error) { + var uuid UUID if len(b) != 16 { - return nil, errors.New("invalid UUID: ", b) + return uuid, errors.New("invalid UUID: ", b) } - uuid := new(UUID) copy(uuid[:], b) return uuid, nil } // ParseString converts an UUID in string form to object. -func ParseString(str string) (*UUID, error) { +func ParseString(str string) (UUID, error) { + var uuid UUID + text := []byte(str) if len(text) < 32 { - return nil, errors.New("invalid UUID: ", str) + return uuid, errors.New("invalid UUID: ", str) } - uuid := new(UUID) b := uuid.Bytes() for _, byteGroup := range byteGroups { @@ -95,7 +97,7 @@ func ParseString(str string) (*UUID, error) { _, err := hex.Decode(b[:byteGroup/2], text[:byteGroup]) if err != nil { - return nil, err + return uuid, err } text = text[byteGroup:] diff --git a/common/uuid/uuid_test.go b/common/uuid/uuid_test.go index f8b90085e..2e7bad4f0 100644 --- a/common/uuid/uuid_test.go +++ b/common/uuid/uuid_test.go @@ -65,7 +65,9 @@ func TestEquals(t *testing.T) { var uuid *UUID = nil var uuid2 *UUID = nil assert(uuid.Equals(uuid2), IsTrue) - assert(uuid.Equals(New()), IsFalse) + + uuid3 := New() + assert(uuid.Equals(&uuid3), IsFalse) } func TestNext(t *testing.T) { @@ -73,5 +75,5 @@ func TestNext(t *testing.T) { uuid := New() uuid2 := uuid.Next() - assert(uuid.Equals(uuid2), IsFalse) + assert(uuid.Equals(&uuid2), IsFalse) } diff --git a/proxy/vmess/encoding/encoding_test.go b/proxy/vmess/encoding/encoding_test.go index 6ed0edc1f..8d0211e48 100644 --- a/proxy/vmess/encoding/encoding_test.go +++ b/proxy/vmess/encoding/encoding_test.go @@ -22,8 +22,9 @@ func TestRequestSerialization(t *testing.T) { Level: 0, Email: "test@v2ray.com", } + id := uuid.New() account := &vmess.Account{ - Id: uuid.New().String(), + Id: id.String(), AlterId: 0, } user.Account = serial.ToTypedMessage(account) diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index df30ae4f9..5553f14e7 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -53,8 +53,9 @@ func (v *userByEmail) Get(email string) (*protocol.User, bool) { v.Lock() user, found = v.cache[email] if !found { + id := uuid.New() account := &vmess.Account{ - Id: uuid.New().String(), + Id: id.String(), AlterId: uint32(v.defaultAlterIDs), } user = &protocol.User{ diff --git a/v2ray.go b/v2ray.go index 7e95eb413..15275f8c8 100644 --- a/v2ray.go +++ b/v2ray.go @@ -37,7 +37,7 @@ type Instance struct { // To make sure V2Ray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional. func New(config *Config) (*Instance, error) { var server = &Instance{ - id: *(uuid.New()), + id: uuid.New(), } if err := config.Transport.Apply(); err != nil { diff --git a/v2ray_test.go b/v2ray_test.go index 51f120c23..7f287a50a 100644 --- a/v2ray_test.go +++ b/v2ray_test.go @@ -21,6 +21,7 @@ func TestV2RayClose(t *testing.T) { assert := With(t) port := net.Port(dice.RollUint16()) + userId := uuid.New() config := &Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&proxyman.InboundConfig{}), @@ -51,7 +52,7 @@ func TestV2RayClose(t *testing.T) { User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ - Id: uuid.New().String(), + Id: userId.String(), }), }, }, From 4b5e41c7832a808c3ad52f41e958ad508add4e01 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 19 Jan 2018 11:08:34 +0100 Subject: [PATCH 20/63] fix UDP handling in Shadowsocks client --- proxy/shadowsocks/client.go | 2 +- proxy/shadowsocks/config.go | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index ff19538e6..3ce880c68 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -155,7 +155,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale User: user, } - if err := buf.Copy(reader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer)); err != nil { + if err := buf.Copy(reader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer), buf.IgnoreReaderError()); err != nil { return newError("failed to transport all UDP response").Base(err) } return nil diff --git a/proxy/shadowsocks/config.go b/proxy/shadowsocks/config.go index 54de878f0..5105e3809 100644 --- a/proxy/shadowsocks/config.go +++ b/proxy/shadowsocks/config.go @@ -140,6 +140,9 @@ func (v *AesCfb) EncodePacket(key []byte, b *buf.Buffer) error { } func (v *AesCfb) DecodePacket(key []byte, b *buf.Buffer) error { + if b.Len() <= v.IVSize() { + return newError("insufficient data: ", b.Len()) + } iv := b.BytesTo(v.IVSize()) stream := crypto.NewAesDecryptionStream(key, iv) stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize())) @@ -203,6 +206,9 @@ func (c *AEADCipher) EncodePacket(key []byte, b *buf.Buffer) error { } func (c *AEADCipher) DecodePacket(key []byte, b *buf.Buffer) error { + if b.Len() <= v.IVSize() { + return newError("insufficient data: ", b.Len()) + } ivLen := c.IVSize() payloadLen := b.Len() auth := c.createAuthenticator(key, b.BytesTo(ivLen)) @@ -253,6 +259,9 @@ func (v *ChaCha20) EncodePacket(key []byte, b *buf.Buffer) error { } func (v *ChaCha20) DecodePacket(key []byte, b *buf.Buffer) error { + if b.Len() <= v.IVSize() { + return newError("insufficient data: ", b.Len()) + } iv := b.BytesTo(v.IVSize()) stream := crypto.NewChaCha20Stream(key, iv) stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize())) From c3dce11c4e46524afbb5b1465cdf4a2a8f6e0ec2 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 19 Jan 2018 14:08:45 +0100 Subject: [PATCH 21/63] fix a typo --- proxy/shadowsocks/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/shadowsocks/config.go b/proxy/shadowsocks/config.go index 5105e3809..4d67b02bf 100644 --- a/proxy/shadowsocks/config.go +++ b/proxy/shadowsocks/config.go @@ -206,7 +206,7 @@ func (c *AEADCipher) EncodePacket(key []byte, b *buf.Buffer) error { } func (c *AEADCipher) DecodePacket(key []byte, b *buf.Buffer) error { - if b.Len() <= v.IVSize() { + if b.Len() <= c.IVSize() { return newError("insufficient data: ", b.Len()) } ivLen := c.IVSize() From 9cd0e90e991d8a78cf8a5584387ff37b00a4f5e5 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 23 Jan 2018 23:12:02 +0100 Subject: [PATCH 22/63] injectable clock --- clock.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ v2ray.go | 6 ++++++ 2 files changed, 65 insertions(+) create mode 100644 clock.go diff --git a/clock.go b/clock.go new file mode 100644 index 000000000..5489cae63 --- /dev/null +++ b/clock.go @@ -0,0 +1,59 @@ +package core + +import ( + "sync" + "time" +) + +// Clock is a V2Ray feature that returns current time. +type Clock interface { + Feature + + // Now returns current time. + Now() time.Time +} + +type syncClock struct { + sync.RWMutex + Clock +} + +func (c *syncClock) Now() time.Time { + c.RLock() + defer c.RUnlock() + + if c.Clock == nil { + return time.Now() + } + + return c.Clock.Now() +} + +func (c *syncClock) Start() error { + c.RLock() + defer c.RUnlock() + + if c.Clock == nil { + return nil + } + + return c.Clock.Start() +} + +func (c *syncClock) Close() { + c.RLock() + defer c.RUnlock() + + if c.Clock == nil { + return + } + + c.Clock.Close() +} + +func (c *syncClock) Set(clock Clock) { + c.Lock() + defer c.Unlock() + + c.Clock = clock +} diff --git a/v2ray.go b/v2ray.go index 15275f8c8..b6a0de9d4 100644 --- a/v2ray.go +++ b/v2ray.go @@ -27,6 +27,7 @@ type Instance struct { router syncRouter ihm syncInboundHandlerManager ohm syncOutboundHandlerManager + clock syncClock features []Feature id uuid.UUID @@ -163,3 +164,8 @@ func (s *Instance) InboundHandlerManager() InboundHandlerManager { func (s *Instance) OutboundHandlerManager() OutboundHandlerManager { return &(s.ohm) } + +// Clock returns the Clock used by this Instance. The returned Clock is always functional. +func (s *Instance) Clock() Clock { + return &(s.clock) +} From 8923159aee77fe9723628c76e73e3d04cdcdd03c Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 24 Jan 2018 15:05:46 +0100 Subject: [PATCH 23/63] commander interface --- commander.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ v2ray.go | 10 +++++++++ 2 files changed, 73 insertions(+) create mode 100644 commander.go diff --git a/commander.go b/commander.go new file mode 100644 index 000000000..f65f0072f --- /dev/null +++ b/commander.go @@ -0,0 +1,63 @@ +package core + +import ( + "sync" + + "google.golang.org/grpc" +) + +// ServiceRegistryCallback is a callback function for registering services. +type ServiceRegistryCallback func(s *grpc.Server) + +// Commander is a feature that accepts commands from external source. +type Commander interface { + Feature + + // RegisterService registers a service into this Commander. + RegisterService(ServiceRegistryCallback) +} + +type syncCommander struct { + sync.RWMutex + Commander +} + +func (c *syncCommander) RegisterService(callback ServiceRegistryCallback) { + c.RLock() + defer c.RUnlock() + + if c.Commander == nil { + return + } + + c.Commander.RegisterService(callback) +} + +func (c *syncCommander) Start() error { + c.RLock() + defer c.RUnlock() + + if c.Commander == nil { + return nil + } + + return c.Commander.Start() +} + +func (c *syncCommander) Close() { + c.RLock() + defer c.RUnlock() + + if c.Commander == nil { + return + } + + c.Commander.Close() +} + +func (c *syncCommander) Set(commander Commander) { + c.Lock() + defer c.Unlock() + + c.Commander = commander +} diff --git a/v2ray.go b/v2ray.go index b6a0de9d4..542deef8a 100644 --- a/v2ray.go +++ b/v2ray.go @@ -28,6 +28,7 @@ type Instance struct { ihm syncInboundHandlerManager ohm syncOutboundHandlerManager clock syncClock + cmd syncCommander features []Feature id uuid.UUID @@ -130,6 +131,10 @@ func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error s.ihm.Set(instance.(InboundHandlerManager)) case OutboundHandlerManager, *OutboundHandlerManager: s.ohm.Set(instance.(OutboundHandlerManager)) + case Clock, *Clock: + s.clock.Set(instance.(Clock)) + case Commander, *Commander: + s.cmd.Set(instance.(Commander)) } s.features = append(s.features, instance) return nil @@ -169,3 +174,8 @@ func (s *Instance) OutboundHandlerManager() OutboundHandlerManager { func (s *Instance) Clock() Clock { return &(s.clock) } + +// Commander returns the Commander used by this Instance. The returned Commander is always functional. +func (s *Instance) Commander() Commander { + return &(s.cmd) +} From 0273b36027df2df8514124c30f062d73255c729f Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 30 Jan 2018 21:38:43 +0100 Subject: [PATCH 24/63] prepare for remove function --- proxy/vmess/vmess.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/proxy/vmess/vmess.go b/proxy/vmess/vmess.go index d40f77f71..0fbefc43c 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -25,7 +25,6 @@ type idEntry struct { id *protocol.ID userIdx int lastSec protocol.Timestamp - lastSecRemoval protocol.Timestamp } type TimedUserValidator struct { @@ -56,25 +55,26 @@ func NewTimedUserValidator(ctx context.Context, hasher protocol.IDHash) protocol func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, idx int, entry *idEntry) { var hashValue [16]byte - var hashValueRemoval [16]byte idHash := v.hasher(entry.id.Bytes()) for entry.lastSec <= nowSec { common.Must2(idHash.Write(entry.lastSec.Bytes(nil))) idHash.Sum(hashValue[:0]) idHash.Reset() - common.Must2(idHash.Write(entry.lastSecRemoval.Bytes(nil))) - idHash.Sum(hashValueRemoval[:0]) - idHash.Reset() - - delete(v.userHash, hashValueRemoval) v.userHash[hashValue] = indexTimePair{ index: idx, timeInc: uint32(entry.lastSec - v.baseTime), } entry.lastSec++ - entry.lastSecRemoval++ + } +} + +func (v *TimedUserValidator) removeExpiredHashes(expire uint32) { + for key, pair := range v.userHash { + if pair.timeInc < expire { + delete(v.userHash, key) + } } } @@ -87,6 +87,11 @@ func (v *TimedUserValidator) updateUserHash(ctx context.Context, interval time.D for _, entry := range v.ids { v.generateNewHashes(nowSec, entry.userIdx, entry) } + + expire := protocol.Timestamp(now.Unix() - cacheDurationSec*3) + if expire > v.baseTime { + v.removeExpiredHashes(uint32(expire - v.baseTime)) + } v.Unlock() case <-ctx.Done(): return @@ -112,7 +117,6 @@ func (v *TimedUserValidator) Add(user *protocol.User) error { id: account.ID, userIdx: idx, lastSec: protocol.Timestamp(nowSec - cacheDurationSec), - lastSecRemoval: protocol.Timestamp(nowSec - cacheDurationSec*3), } v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry) v.ids = append(v.ids, entry) @@ -121,7 +125,6 @@ func (v *TimedUserValidator) Add(user *protocol.User) error { id: alterid, userIdx: idx, lastSec: protocol.Timestamp(nowSec - cacheDurationSec), - lastSecRemoval: protocol.Timestamp(nowSec - cacheDurationSec*3), } v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry) v.ids = append(v.ids, entry) From cf832a42729d6c153674d88ef2d6873da638af02 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 31 Jan 2018 13:23:23 +0100 Subject: [PATCH 25/63] adjust init sequence --- proxy/vmess/inbound/inbound.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 5553f14e7..9949383a2 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -82,13 +82,6 @@ type Handler struct { // New creates a new VMess inbound handler. func New(ctx context.Context, config *Config) (*Handler, error) { - allowedClients := vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash) - for _, user := range config.User { - if err := allowedClients.Add(user); err != nil { - return nil, newError("failed to initiate user").Base(err) - } - } - v := core.FromContext(ctx) if v == nil { return nil, newError("V is not in context.") @@ -97,12 +90,18 @@ func New(ctx context.Context, config *Config) (*Handler, error) { handler := &Handler{ policyManager: v.PolicyManager(), inboundHandlerManager: v.InboundHandlerManager(), - clients: allowedClients, + clients: vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash), detours: config.Detour, usersByEmail: newUserByEmail(config.User, config.GetDefaultValue()), sessionHistory: encoding.NewSessionHistory(ctx), } + for _, user := range config.User { + if err := handler.AddUser(ctx, user); err != nil { + return nil, newError("failed to initiate user").Base(err) + } + } + return handler, nil } @@ -121,6 +120,10 @@ func (h *Handler) GetUser(email string) *protocol.User { return user } +func (h *Handler) AddUser(ctx context.Context, user *protocol.User) error { + return h.clients.Add(user) +} + func transferRequest(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output ray.OutputStream) error { defer output.Close() From 384844f898cb4f8adf31f2473f94384d4e441ca6 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 2 Feb 2018 22:35:18 +0100 Subject: [PATCH 26/63] fix #840 --- app/proxyman/inbound/worker.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 24cfc2653..51bdc6ff7 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -121,6 +121,7 @@ type udpConn struct { output func([]byte) (int, error) remote net.Addr local net.Addr + ctx context.Context cancel context.CancelFunc } @@ -129,13 +130,14 @@ func (c *udpConn) updateActivity() { } func (c *udpConn) Read(buf []byte) (int, error) { - in, open := <-c.input - if !open { + select { + case in := <-c.input: + defer in.Release() + c.updateActivity() + return copy(buf, in.Bytes()), nil + case <-c.ctx.Done(): return 0, io.EOF } - defer in.Release() - c.updateActivity() - return copy(buf, in.Bytes()), nil } // Write implements io.Writer. @@ -236,6 +238,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest go func() { ctx := w.ctx ctx, cancel := context.WithCancel(ctx) + conn.ctx = ctx conn.cancel = cancel if originalDest.IsValid() { ctx = proxy.ContextWithOriginalTarget(ctx, originalDest) From 1e6d5561cc9ef279ffe9a061408f712be012a724 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 5 Feb 2018 23:38:24 +0100 Subject: [PATCH 27/63] prototype of commander --- app/commander/commander.go | 63 +++ app/commander/config.pb.go | 51 +++ app/commander/config.proto | 11 + app/commander/errors.generated.go | 5 + app/commander/outbound.go | 65 +++ app/proxyman/command/command.go | 139 ++++++ app/proxyman/command/command.pb.go | 520 +++++++++++++++++++++++ app/proxyman/command/command.proto | 80 ++++ app/proxyman/command/doc.go | 3 + app/proxyman/command/errors.generated.go | 5 + app/proxyman/inbound/always.go | 4 + app/proxyman/inbound/inbound.go | 45 +- app/proxyman/outbound/handler.go | 6 +- app/proxyman/outbound/outbound.go | 15 + dial.go | 2 +- network.go | 6 + proxy/proxy.go | 18 + proxy/vmess/inbound/inbound.go | 4 + transport/ray/connection.go | 71 +++- v2ray.go | 17 +- 20 files changed, 1097 insertions(+), 33 deletions(-) create mode 100644 app/commander/commander.go create mode 100644 app/commander/config.pb.go create mode 100644 app/commander/config.proto create mode 100644 app/commander/errors.generated.go create mode 100644 app/commander/outbound.go create mode 100644 app/proxyman/command/command.go create mode 100644 app/proxyman/command/command.pb.go create mode 100644 app/proxyman/command/command.proto create mode 100644 app/proxyman/command/doc.go create mode 100644 app/proxyman/command/errors.generated.go diff --git a/app/commander/commander.go b/app/commander/commander.go new file mode 100644 index 000000000..43a4aa929 --- /dev/null +++ b/app/commander/commander.go @@ -0,0 +1,63 @@ +package commander + +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg commander -path App,Commander + +import ( + "context" + "net" + "sync" + + "google.golang.org/grpc" + "v2ray.com/core" +) + +type Commander struct { + sync.Mutex + server *grpc.Server + config Config + ohm core.OutboundHandlerManager + callbacks []core.ServiceRegistryCallback +} + +func (c *Commander) RegisterService(callback core.ServiceRegistryCallback) { + c.Lock() + defer c.Unlock() + + if callback == nil { + return + } + + c.callbacks = append(c.callbacks, callback) +} + +func (c *Commander) Start() error { + c.Lock() + c.server = grpc.NewServer() + for _, callback := range c.callbacks { + callback(c.server) + } + c.Unlock() + + listener := &OutboundListener{ + buffer: make(chan net.Conn, 4), + } + + c.server.Serve(listener) + + c.ohm.RemoveHandler(context.Background(), c.config.Tag) + c.ohm.AddHandler(context.Background(), &CommanderOutbound{ + tag: c.config.Tag, + listener: listener, + }) + return nil +} + +func (c *Commander) Close() { + c.Lock() + defer c.Unlock() + + if c.server != nil { + c.server.Stop() + c.server = nil + } +} diff --git a/app/commander/config.pb.go b/app/commander/config.pb.go new file mode 100644 index 000000000..dbb64bdf8 --- /dev/null +++ b/app/commander/config.pb.go @@ -0,0 +1,51 @@ +package commander + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Config struct { + Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` +} + +func (m *Config) Reset() { *m = Config{} } +func (m *Config) String() string { return proto.CompactTextString(m) } +func (*Config) ProtoMessage() {} +func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Config) GetTag() string { + if m != nil { + return m.Tag + } + return "" +} + +func init() { + proto.RegisterType((*Config)(nil), "v2ray.core.app.commander.Config") +} + +func init() { proto.RegisterFile("v2ray.com/core/app/commander/config.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 143 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2c, 0x33, 0x2a, 0x4a, + 0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0x4f, + 0xce, 0xcf, 0xcd, 0x4d, 0xcc, 0x4b, 0x49, 0x2d, 0xd2, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, + 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x80, 0x29, 0x2d, 0x4a, 0xd5, 0x4b, 0x2c, 0x28, 0xd0, + 0x83, 0x2b, 0x53, 0x92, 0xe2, 0x62, 0x73, 0x06, 0xab, 0x14, 0x12, 0xe0, 0x62, 0x2e, 0x49, 0x4c, + 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x9d, 0xdc, 0xb8, 0x64, 0x92, 0xf3, 0x73, + 0xf5, 0x70, 0xe9, 0x0d, 0x60, 0x8c, 0xe2, 0x84, 0x73, 0x56, 0x31, 0x49, 0x84, 0x19, 0x05, 0x25, + 0x56, 0xea, 0x39, 0x83, 0xd4, 0x39, 0x16, 0x14, 0xe8, 0x39, 0xc3, 0xa4, 0x92, 0xd8, 0xc0, 0x8e, + 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x36, 0x74, 0x98, 0x19, 0xb1, 0x00, 0x00, 0x00, +} diff --git a/app/commander/config.proto b/app/commander/config.proto new file mode 100644 index 000000000..56a7b7745 --- /dev/null +++ b/app/commander/config.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package v2ray.core.app.commander; +option csharp_namespace = "V2Ray.Core.App.Commander"; +option go_package = "commander"; +option java_package = "com.v2ray.core.app.commander"; +option java_multiple_files = true; + +message Config { + string tag = 1; +} diff --git a/app/commander/errors.generated.go b/app/commander/errors.generated.go new file mode 100644 index 000000000..fd7b91797 --- /dev/null +++ b/app/commander/errors.generated.go @@ -0,0 +1,5 @@ +package commander + +import "v2ray.com/core/common/errors" + +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Commander") } diff --git a/app/commander/outbound.go b/app/commander/outbound.go new file mode 100644 index 000000000..1338c7451 --- /dev/null +++ b/app/commander/outbound.go @@ -0,0 +1,65 @@ +package commander + +import ( + "context" + "net" + + "v2ray.com/core/common/signal" + "v2ray.com/core/transport/ray" +) + +type OutboundListener struct { + buffer chan net.Conn +} + +func (l *OutboundListener) add(conn net.Conn) { + select { + case l.buffer <- conn: + default: + conn.Close() + } +} + +func (l *OutboundListener) Accept() (net.Conn, error) { + c, open := <-l.buffer + if !open { + return nil, newError("listener closed") + } + return c, nil +} + +func (l *OutboundListener) Close() error { + close(l.buffer) + return nil +} + +func (l *OutboundListener) Addr() net.Addr { + return &net.TCPAddr{ + IP: net.IP{0, 0, 0, 0}, + Port: 0, + } +} + +type CommanderOutbound struct { + tag string + listener *OutboundListener +} + +func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) { + closeSignal := signal.NewNotifier() + c := ray.NewConnection(r.OutboundInput(), r.OutboundOutput(), ray.ConnCloseSignal(closeSignal)) + co.listener.add(c) + <-closeSignal.Wait() + + return +} + +func (co *CommanderOutbound) Tag() string { + return co.tag +} + +func (co *CommanderOutbound) Start() error { + return nil +} + +func (co *CommanderOutbound) Close() {} \ No newline at end of file diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go new file mode 100644 index 000000000..7ee973c4f --- /dev/null +++ b/app/proxyman/command/command.go @@ -0,0 +1,139 @@ +package command + +import ( + "context" + + grpc "google.golang.org/grpc" + "v2ray.com/core" + "v2ray.com/core/common" + "v2ray.com/core/proxy" +) + +type InboundOperation interface { + ApplyInbound(context.Context, core.InboundHandler) error +} + +type OutboundOperation interface { + ApplyOutbound(context.Context, core.OutboundHandler) error +} + +func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler core.InboundHandler) error { + getInbound, ok := handler.(proxy.GetInbound) + if !ok { + return newError("can't get inbound proxy from handler") + } + p := getInbound.GetInbound() + um, ok := p.(proxy.UserManager) + if !ok { + return newError("proxy is not an UserManager") + } + return um.AddUser(ctx, op.User) +} + +func (op *AddUserOperation) ApplyOutbound(ctx context.Context, handler core.OutboundHandler) error { + getOutbound, ok := handler.(proxy.GetOutbound) + if !ok { + return newError("can't get outbound proxy from handler") + } + p := getOutbound.GetOutbound() + um, ok := p.(proxy.UserManager) + if !ok { + return newError("proxy in not an UserManager") + } + return um.AddUser(ctx, op.User) +} + +type handlerServer struct { + s *core.Instance + ihm core.InboundHandlerManager + ohm core.OutboundHandlerManager +} + +func (s *handlerServer) AddInbound(ctx context.Context, request *AddInboundRequest) (*AddInboundResponse, error) { + rawHandler, err := s.s.CreateObject(request.Inbound) + if err != nil { + return nil, err + } + handler, ok := rawHandler.(core.InboundHandler) + if !ok { + return nil, newError("not an InboundHandler.") + } + return &AddInboundResponse{}, s.ihm.AddHandler(ctx, handler) +} + +func (s *handlerServer) RemoveInbound(ctx context.Context, request *RemoveInboundRequest) (*RemoveInboundResponse, error) { + return &RemoveInboundResponse{}, s.ihm.RemoveHandler(ctx, request.Tag) +} + +func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundRequest) (*AlterInboundResponse, error) { + rawOperation, err := request.Operation.GetInstance() + if err != nil { + return nil, newError("unknown operation").Base(err) + } + operation, ok := rawOperation.(InboundOperation) + if !ok { + return nil, newError("not an inbound operation") + } + + handler, err := s.ihm.GetHandler(ctx, request.Tag) + if err != nil { + return nil, newError("failed to get handler: ", request.Tag).Base(err) + } + + return &AlterInboundResponse{}, operation.ApplyInbound(ctx, handler) +} + +func (s *handlerServer) AddOutbound(ctx context.Context, request *AddOutboundRequest) (*AddOutboundResponse, error) { + rawHandler, err := s.s.CreateObject(request.Outbound) + if err != nil { + return nil, err + } + handler, ok := rawHandler.(core.OutboundHandler) + if !ok { + return nil, newError("not an OutboundHandler.") + } + return &AddOutboundResponse{}, s.ohm.AddHandler(ctx, handler) +} + +func (s *handlerServer) RemoveOutbound(ctx context.Context, request *RemoveOutboundRequest) (*RemoveOutboundResponse, error) { + return &RemoveOutboundResponse{}, s.ohm.RemoveHandler(ctx, request.Tag) +} + +func (s *handlerServer) AlterOutbound(ctx context.Context, request *AlterOutboundRequest) (*AlterOutboundResponse, error) { + rawOperation, err := request.Operation.GetInstance() + if err != nil { + return nil, newError("unknown operation").Base(err) + } + operation, ok := rawOperation.(OutboundOperation) + if !ok { + return nil, newError("not an outbound operation") + } + + handler := s.ohm.GetHandler(request.Tag) + return &AlterOutboundResponse{}, operation.ApplyOutbound(ctx, handler) +} + +type feature struct{} + +func (*feature) Start() error { + return nil +} + +func (*feature) Close() {} + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { + s := core.FromContext(ctx) + if s == nil { + return nil, newError("V is not in context.") + } + s.Commander().RegisterService(func(server *grpc.Server) { + RegisterHandlerServiceServer(server, &handlerServer{ + s: s, + ihm: s.InboundHandlerManager(), + ohm: s.OutboundHandlerManager(), + }) + }) + return &feature{}, nil + })) +} diff --git a/app/proxyman/command/command.pb.go b/app/proxyman/command/command.pb.go new file mode 100644 index 000000000..fe0c1071d --- /dev/null +++ b/app/proxyman/command/command.pb.go @@ -0,0 +1,520 @@ +package command + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import v2ray_core_common_protocol "v2ray.com/core/common/protocol" +import v2ray_core_common_serial "v2ray.com/core/common/serial" +import v2ray_core "v2ray.com/core" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type AddUserOperation struct { + User *v2ray_core_common_protocol.User `protobuf:"bytes,1,opt,name=user" json:"user,omitempty"` +} + +func (m *AddUserOperation) Reset() { *m = AddUserOperation{} } +func (m *AddUserOperation) String() string { return proto.CompactTextString(m) } +func (*AddUserOperation) ProtoMessage() {} +func (*AddUserOperation) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *AddUserOperation) GetUser() *v2ray_core_common_protocol.User { + if m != nil { + return m.User + } + return nil +} + +type RemoveUserOperation struct { + Email string `protobuf:"bytes,1,opt,name=email" json:"email,omitempty"` +} + +func (m *RemoveUserOperation) Reset() { *m = RemoveUserOperation{} } +func (m *RemoveUserOperation) String() string { return proto.CompactTextString(m) } +func (*RemoveUserOperation) ProtoMessage() {} +func (*RemoveUserOperation) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *RemoveUserOperation) GetEmail() string { + if m != nil { + return m.Email + } + return "" +} + +type AddInboundRequest struct { + Inbound *v2ray_core.InboundHandlerConfig `protobuf:"bytes,1,opt,name=inbound" json:"inbound,omitempty"` +} + +func (m *AddInboundRequest) Reset() { *m = AddInboundRequest{} } +func (m *AddInboundRequest) String() string { return proto.CompactTextString(m) } +func (*AddInboundRequest) ProtoMessage() {} +func (*AddInboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *AddInboundRequest) GetInbound() *v2ray_core.InboundHandlerConfig { + if m != nil { + return m.Inbound + } + return nil +} + +type AddInboundResponse struct { +} + +func (m *AddInboundResponse) Reset() { *m = AddInboundResponse{} } +func (m *AddInboundResponse) String() string { return proto.CompactTextString(m) } +func (*AddInboundResponse) ProtoMessage() {} +func (*AddInboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +type RemoveInboundRequest struct { + Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` +} + +func (m *RemoveInboundRequest) Reset() { *m = RemoveInboundRequest{} } +func (m *RemoveInboundRequest) String() string { return proto.CompactTextString(m) } +func (*RemoveInboundRequest) ProtoMessage() {} +func (*RemoveInboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *RemoveInboundRequest) GetTag() string { + if m != nil { + return m.Tag + } + return "" +} + +type RemoveInboundResponse struct { +} + +func (m *RemoveInboundResponse) Reset() { *m = RemoveInboundResponse{} } +func (m *RemoveInboundResponse) String() string { return proto.CompactTextString(m) } +func (*RemoveInboundResponse) ProtoMessage() {} +func (*RemoveInboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +type AlterInboundRequest struct { + Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` + Operation *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=operation" json:"operation,omitempty"` +} + +func (m *AlterInboundRequest) Reset() { *m = AlterInboundRequest{} } +func (m *AlterInboundRequest) String() string { return proto.CompactTextString(m) } +func (*AlterInboundRequest) ProtoMessage() {} +func (*AlterInboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *AlterInboundRequest) GetTag() string { + if m != nil { + return m.Tag + } + return "" +} + +func (m *AlterInboundRequest) GetOperation() *v2ray_core_common_serial.TypedMessage { + if m != nil { + return m.Operation + } + return nil +} + +type AlterInboundResponse struct { +} + +func (m *AlterInboundResponse) Reset() { *m = AlterInboundResponse{} } +func (m *AlterInboundResponse) String() string { return proto.CompactTextString(m) } +func (*AlterInboundResponse) ProtoMessage() {} +func (*AlterInboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +type AddOutboundRequest struct { + Outbound *v2ray_core.OutboundHandlerConfig `protobuf:"bytes,1,opt,name=outbound" json:"outbound,omitempty"` +} + +func (m *AddOutboundRequest) Reset() { *m = AddOutboundRequest{} } +func (m *AddOutboundRequest) String() string { return proto.CompactTextString(m) } +func (*AddOutboundRequest) ProtoMessage() {} +func (*AddOutboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *AddOutboundRequest) GetOutbound() *v2ray_core.OutboundHandlerConfig { + if m != nil { + return m.Outbound + } + return nil +} + +type AddOutboundResponse struct { +} + +func (m *AddOutboundResponse) Reset() { *m = AddOutboundResponse{} } +func (m *AddOutboundResponse) String() string { return proto.CompactTextString(m) } +func (*AddOutboundResponse) ProtoMessage() {} +func (*AddOutboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +type RemoveOutboundRequest struct { + Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` +} + +func (m *RemoveOutboundRequest) Reset() { *m = RemoveOutboundRequest{} } +func (m *RemoveOutboundRequest) String() string { return proto.CompactTextString(m) } +func (*RemoveOutboundRequest) ProtoMessage() {} +func (*RemoveOutboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *RemoveOutboundRequest) GetTag() string { + if m != nil { + return m.Tag + } + return "" +} + +type RemoveOutboundResponse struct { +} + +func (m *RemoveOutboundResponse) Reset() { *m = RemoveOutboundResponse{} } +func (m *RemoveOutboundResponse) String() string { return proto.CompactTextString(m) } +func (*RemoveOutboundResponse) ProtoMessage() {} +func (*RemoveOutboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +type AlterOutboundRequest struct { + Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` + Operation *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=operation" json:"operation,omitempty"` +} + +func (m *AlterOutboundRequest) Reset() { *m = AlterOutboundRequest{} } +func (m *AlterOutboundRequest) String() string { return proto.CompactTextString(m) } +func (*AlterOutboundRequest) ProtoMessage() {} +func (*AlterOutboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *AlterOutboundRequest) GetTag() string { + if m != nil { + return m.Tag + } + return "" +} + +func (m *AlterOutboundRequest) GetOperation() *v2ray_core_common_serial.TypedMessage { + if m != nil { + return m.Operation + } + return nil +} + +type AlterOutboundResponse struct { +} + +func (m *AlterOutboundResponse) Reset() { *m = AlterOutboundResponse{} } +func (m *AlterOutboundResponse) String() string { return proto.CompactTextString(m) } +func (*AlterOutboundResponse) ProtoMessage() {} +func (*AlterOutboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +type Config struct { +} + +func (m *Config) Reset() { *m = Config{} } +func (m *Config) String() string { return proto.CompactTextString(m) } +func (*Config) ProtoMessage() {} +func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func init() { + proto.RegisterType((*AddUserOperation)(nil), "v2ray.core.app.proxyman.command.AddUserOperation") + proto.RegisterType((*RemoveUserOperation)(nil), "v2ray.core.app.proxyman.command.RemoveUserOperation") + proto.RegisterType((*AddInboundRequest)(nil), "v2ray.core.app.proxyman.command.AddInboundRequest") + proto.RegisterType((*AddInboundResponse)(nil), "v2ray.core.app.proxyman.command.AddInboundResponse") + proto.RegisterType((*RemoveInboundRequest)(nil), "v2ray.core.app.proxyman.command.RemoveInboundRequest") + proto.RegisterType((*RemoveInboundResponse)(nil), "v2ray.core.app.proxyman.command.RemoveInboundResponse") + proto.RegisterType((*AlterInboundRequest)(nil), "v2ray.core.app.proxyman.command.AlterInboundRequest") + proto.RegisterType((*AlterInboundResponse)(nil), "v2ray.core.app.proxyman.command.AlterInboundResponse") + proto.RegisterType((*AddOutboundRequest)(nil), "v2ray.core.app.proxyman.command.AddOutboundRequest") + proto.RegisterType((*AddOutboundResponse)(nil), "v2ray.core.app.proxyman.command.AddOutboundResponse") + proto.RegisterType((*RemoveOutboundRequest)(nil), "v2ray.core.app.proxyman.command.RemoveOutboundRequest") + proto.RegisterType((*RemoveOutboundResponse)(nil), "v2ray.core.app.proxyman.command.RemoveOutboundResponse") + proto.RegisterType((*AlterOutboundRequest)(nil), "v2ray.core.app.proxyman.command.AlterOutboundRequest") + proto.RegisterType((*AlterOutboundResponse)(nil), "v2ray.core.app.proxyman.command.AlterOutboundResponse") + proto.RegisterType((*Config)(nil), "v2ray.core.app.proxyman.command.Config") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for HandlerService service + +type HandlerServiceClient interface { + AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) + RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) + AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) + AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) + RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) + AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) +} + +type handlerServiceClient struct { + cc *grpc.ClientConn +} + +func NewHandlerServiceClient(cc *grpc.ClientConn) HandlerServiceClient { + return &handlerServiceClient{cc} +} + +func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) { + out := new(AddInboundResponse) + err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/AddInbound", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) { + out := new(RemoveInboundResponse) + err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/RemoveInbound", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) { + out := new(AlterInboundResponse) + err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/AlterInbound", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) { + out := new(AddOutboundResponse) + err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/AddOutbound", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) { + out := new(RemoveOutboundResponse) + err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/RemoveOutbound", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *handlerServiceClient) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) { + out := new(AlterOutboundResponse) + err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/AlterOutbound", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for HandlerService service + +type HandlerServiceServer interface { + AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) + RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error) + AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) + AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error) + RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error) + AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) +} + +func RegisterHandlerServiceServer(s *grpc.Server, srv HandlerServiceServer) { + s.RegisterService(&_HandlerService_serviceDesc, srv) +} + +func _HandlerService_AddInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddInboundRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HandlerServiceServer).AddInbound(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/AddInbound", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HandlerServiceServer).AddInbound(ctx, req.(*AddInboundRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HandlerService_RemoveInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveInboundRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HandlerServiceServer).RemoveInbound(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/RemoveInbound", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HandlerServiceServer).RemoveInbound(ctx, req.(*RemoveInboundRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HandlerService_AlterInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AlterInboundRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HandlerServiceServer).AlterInbound(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/AlterInbound", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HandlerServiceServer).AlterInbound(ctx, req.(*AlterInboundRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HandlerService_AddOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddOutboundRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HandlerServiceServer).AddOutbound(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/AddOutbound", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HandlerServiceServer).AddOutbound(ctx, req.(*AddOutboundRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HandlerService_RemoveOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveOutboundRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HandlerServiceServer).RemoveOutbound(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/RemoveOutbound", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HandlerServiceServer).RemoveOutbound(ctx, req.(*RemoveOutboundRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AlterOutboundRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HandlerServiceServer).AlterOutbound(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/AlterOutbound", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HandlerServiceServer).AlterOutbound(ctx, req.(*AlterOutboundRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _HandlerService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v2ray.core.app.proxyman.command.HandlerService", + HandlerType: (*HandlerServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AddInbound", + Handler: _HandlerService_AddInbound_Handler, + }, + { + MethodName: "RemoveInbound", + Handler: _HandlerService_RemoveInbound_Handler, + }, + { + MethodName: "AlterInbound", + Handler: _HandlerService_AlterInbound_Handler, + }, + { + MethodName: "AddOutbound", + Handler: _HandlerService_AddOutbound_Handler, + }, + { + MethodName: "RemoveOutbound", + Handler: _HandlerService_RemoveOutbound_Handler, + }, + { + MethodName: "AlterOutbound", + Handler: _HandlerService_AlterOutbound_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "v2ray.com/core/app/proxyman/command/command.proto", +} + +func init() { proto.RegisterFile("v2ray.com/core/app/proxyman/command/command.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 557 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xdf, 0x6b, 0xd3, 0x40, + 0x1c, 0xb7, 0x53, 0xbb, 0xed, 0x3b, 0x1d, 0xf3, 0xda, 0x6e, 0x25, 0x3e, 0x6c, 0x46, 0x90, 0x0d, + 0xe1, 0xa2, 0x59, 0x37, 0x41, 0xf0, 0xa1, 0xd6, 0x87, 0xf9, 0x20, 0x1d, 0x99, 0xfa, 0xe0, 0x8b, + 0xdc, 0x92, 0xb3, 0x04, 0x92, 0xbb, 0xf3, 0x92, 0x56, 0x2b, 0x08, 0x82, 0xff, 0x80, 0x7f, 0x87, + 0x7f, 0xa5, 0x24, 0x77, 0xd7, 0x25, 0x69, 0x21, 0x0d, 0xf8, 0xd4, 0xf4, 0xfa, 0xf9, 0xf5, 0xfd, + 0xde, 0x27, 0x14, 0x9e, 0xcf, 0x5c, 0x49, 0xe6, 0xd8, 0xe7, 0xb1, 0xe3, 0x73, 0x49, 0x1d, 0x22, + 0x84, 0x23, 0x24, 0xff, 0x3e, 0x8f, 0x09, 0x73, 0x7c, 0x1e, 0xc7, 0x84, 0x05, 0xe6, 0x13, 0x0b, + 0xc9, 0x53, 0x8e, 0x0e, 0x0d, 0x45, 0x52, 0x4c, 0x84, 0xc0, 0x06, 0x8e, 0x35, 0xcc, 0x3a, 0xa9, + 0x68, 0x66, 0xe7, 0x9c, 0x39, 0x39, 0xdb, 0xe7, 0x91, 0x33, 0x4d, 0xa8, 0x54, 0x5a, 0xd6, 0xb3, + 0xd5, 0xd0, 0x84, 0xca, 0x90, 0x44, 0x4e, 0x3a, 0x17, 0x34, 0xf8, 0x1c, 0xd3, 0x24, 0x21, 0x13, + 0xaa, 0x19, 0x0f, 0x97, 0x18, 0xec, 0x4b, 0x38, 0x51, 0x3f, 0xda, 0x17, 0xb0, 0x37, 0x0c, 0x82, + 0x0f, 0x09, 0x95, 0x63, 0x41, 0x25, 0x49, 0x43, 0xce, 0xd0, 0x00, 0xee, 0x64, 0x86, 0xfd, 0xd6, + 0x51, 0xeb, 0x78, 0xc7, 0x3d, 0xc2, 0x85, 0xf4, 0xca, 0x0d, 0x9b, 0x60, 0x38, 0x23, 0x7a, 0x39, + 0xda, 0x7e, 0x0a, 0x1d, 0x8f, 0xc6, 0x7c, 0x46, 0xcb, 0x62, 0x5d, 0xb8, 0x4b, 0x63, 0x12, 0x46, + 0xb9, 0xda, 0xb6, 0xa7, 0xbe, 0xd8, 0x63, 0x78, 0x30, 0x0c, 0x82, 0xb7, 0xec, 0x9a, 0x4f, 0x59, + 0xe0, 0xd1, 0xaf, 0x53, 0x9a, 0xa4, 0xe8, 0x25, 0x6c, 0x86, 0xea, 0x64, 0x95, 0xb5, 0x06, 0x5f, + 0x10, 0x16, 0x44, 0x54, 0x8e, 0xf2, 0x21, 0x3c, 0x43, 0xb0, 0xbb, 0x80, 0x8a, 0x82, 0x89, 0xe0, + 0x2c, 0xa1, 0xf6, 0x31, 0x74, 0x55, 0xa6, 0x8a, 0xd3, 0x1e, 0xdc, 0x4e, 0xc9, 0x44, 0x47, 0xca, + 0x1e, 0xed, 0x03, 0xe8, 0x55, 0x90, 0x5a, 0x22, 0x86, 0xce, 0x30, 0x4a, 0xa9, 0xac, 0x53, 0x40, + 0x6f, 0x60, 0x9b, 0x9b, 0xa9, 0xfb, 0x1b, 0x79, 0xfe, 0x27, 0x2b, 0x56, 0xa7, 0x2e, 0x0a, 0xbf, + 0xcf, 0x2e, 0xea, 0x9d, 0xba, 0x27, 0xef, 0x86, 0x68, 0xef, 0x43, 0xb7, 0x6c, 0xa7, 0x63, 0x5c, + 0xe5, 0xf3, 0x8d, 0xa7, 0x69, 0x29, 0xc5, 0x2b, 0xd8, 0xe2, 0xfa, 0x48, 0xaf, 0xec, 0x51, 0xd1, + 0xd2, 0xc0, 0xcb, 0x3b, 0x5b, 0x50, 0xec, 0x1e, 0x74, 0x4a, 0xa2, 0xda, 0xeb, 0xc4, 0xec, 0xa2, + 0x6a, 0xb7, 0xbc, 0xb6, 0x3e, 0xec, 0x57, 0xa1, 0x5a, 0x84, 0xe9, 0x41, 0x6a, 0x35, 0xfe, 0xd3, + 0xe2, 0x0e, 0xa0, 0x57, 0xf1, 0xd3, 0x41, 0xb6, 0xa0, 0xad, 0x06, 0x77, 0xff, 0xb4, 0x61, 0x57, + 0xaf, 0xe2, 0x8a, 0xca, 0x59, 0xe8, 0x53, 0xf4, 0x0d, 0xe0, 0xa6, 0x36, 0xc8, 0xc5, 0x35, 0x2f, + 0x2a, 0x5e, 0x2a, 0xad, 0x75, 0xda, 0x88, 0xa3, 0x33, 0xdd, 0x42, 0xbf, 0x5a, 0x70, 0xbf, 0x54, + 0x38, 0x74, 0x56, 0x2b, 0xb4, 0xaa, 0xca, 0xd6, 0x79, 0x53, 0xda, 0x22, 0xc2, 0x4f, 0xb8, 0x57, + 0xac, 0x1a, 0x1a, 0xd4, 0x4f, 0xb2, 0xfc, 0x22, 0x58, 0x67, 0x0d, 0x59, 0x0b, 0xfb, 0x1f, 0xb0, + 0x53, 0x28, 0x1f, 0x5a, 0x6b, 0x8f, 0x95, 0x32, 0x59, 0x83, 0x66, 0xa4, 0x85, 0xf7, 0xef, 0x16, + 0xec, 0x96, 0x7b, 0x8b, 0xd6, 0xdd, 0x63, 0x35, 0xc2, 0x8b, 0xc6, 0xbc, 0x52, 0x07, 0x4a, 0x9d, + 0x45, 0x6b, 0x2e, 0xb3, 0x9a, 0xe1, 0xbc, 0x29, 0xcd, 0x44, 0x78, 0xed, 0xc1, 0x63, 0x9f, 0xc7, + 0x75, 0xf4, 0xcb, 0xd6, 0xa7, 0x4d, 0xfd, 0xf8, 0x77, 0xe3, 0xf0, 0xa3, 0xeb, 0x91, 0x39, 0x1e, + 0x65, 0xe0, 0xa1, 0x10, 0xf8, 0xd2, 0x80, 0x47, 0x0a, 0x71, 0xdd, 0xce, 0xff, 0x1d, 0x4e, 0xff, + 0x05, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x05, 0xaa, 0x44, 0x29, 0x07, 0x00, 0x00, +} diff --git a/app/proxyman/command/command.proto b/app/proxyman/command/command.proto new file mode 100644 index 000000000..c40c8ec25 --- /dev/null +++ b/app/proxyman/command/command.proto @@ -0,0 +1,80 @@ +syntax = "proto3"; + +package v2ray.core.app.proxyman.command; +option csharp_namespace = "V2Ray.Core.App.Proxyman.Command"; +option go_package = "command"; +option java_package = "com.v2ray.core.app.proxyman.command"; +option java_multiple_files = true; + +import "v2ray.com/core/common/protocol/user.proto"; +import "v2ray.com/core/common/serial/typed_message.proto"; +import "v2ray.com/core/config.proto"; + +message AddUserOperation { + v2ray.core.common.protocol.User user = 1; +} + +message RemoveUserOperation { + string email = 1; +} + +message AddInboundRequest { + core.InboundHandlerConfig inbound = 1; +} + +message AddInboundResponse{ + +} + +message RemoveInboundRequest { + string tag = 1; +} + +message RemoveInboundResponse {} + +message AlterInboundRequest { + string tag = 1; + v2ray.core.common.serial.TypedMessage operation = 2; +} + +message AlterInboundResponse { +} + +message AddOutboundRequest { + core.OutboundHandlerConfig outbound = 1; +} + +message AddOutboundResponse { + +} + +message RemoveOutboundRequest { + string tag = 1; +} + +message RemoveOutboundResponse { +} + +message AlterOutboundRequest { + string tag = 1; + v2ray.core.common.serial.TypedMessage operation = 2; +} + +message AlterOutboundResponse { +} + +service HandlerService { + rpc AddInbound(AddInboundRequest) returns (AddInboundResponse) {} + + rpc RemoveInbound(RemoveInboundRequest) returns (RemoveInboundResponse) {} + + rpc AlterInbound(AlterInboundRequest) returns (AlterInboundResponse) {} + + rpc AddOutbound(AddOutboundRequest) returns (AddOutboundResponse) {} + + rpc RemoveOutbound(RemoveOutboundRequest) returns (RemoveOutboundResponse) {} + + rpc AlterOutbound(AlterOutboundRequest) returns (AlterOutboundResponse) {} +} + +message Config {} \ No newline at end of file diff --git a/app/proxyman/command/doc.go b/app/proxyman/command/doc.go new file mode 100644 index 000000000..8c18efaf4 --- /dev/null +++ b/app/proxyman/command/doc.go @@ -0,0 +1,3 @@ +package command + +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Proxyman,Command diff --git a/app/proxyman/command/errors.generated.go b/app/proxyman/command/errors.generated.go new file mode 100644 index 000000000..3cbe9949a --- /dev/null +++ b/app/proxyman/command/errors.generated.go @@ -0,0 +1,5 @@ +package command + +import "v2ray.com/core/common/errors" + +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Proxyman", "Command") } diff --git a/app/proxyman/inbound/always.go b/app/proxyman/inbound/always.go index 7a49ce592..ed0855743 100644 --- a/app/proxyman/inbound/always.go +++ b/app/proxyman/inbound/always.go @@ -93,3 +93,7 @@ func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, func (h *AlwaysOnInboundHandler) Tag() string { return h.tag } + +func (h *AlwaysOnInboundHandler) GetInbound() proxy.Inbound { + return h.proxy +} diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index f52a4cdae..2cf37dedb 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -4,6 +4,7 @@ package inbound import ( "context" + "sync" "v2ray.com/core" "v2ray.com/core/app/proxyman" @@ -12,8 +13,9 @@ import ( // Manager is to manage all inbound handlers. type Manager struct { - handlers []core.InboundHandler - taggedHandlers map[string]core.InboundHandler + sync.RWMutex + untaggedHandler []core.InboundHandler + taggedHandlers map[string]core.InboundHandler } func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) { @@ -31,15 +33,22 @@ func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) } func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) error { - m.handlers = append(m.handlers, handler) + m.Lock() + defer m.Unlock() + tag := handler.Tag() if len(tag) > 0 { m.taggedHandlers[tag] = handler + } else { + m.untaggedHandler = append(m.untaggedHandler, handler) } return nil } func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) { + m.RLock() + defer m.RUnlock() + handler, found := m.taggedHandlers[tag] if !found { return nil, newError("handler not found: ", tag) @@ -47,8 +56,31 @@ func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandl return handler, nil } +func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { + if len(tag) == 0 { + return core.ErrNoClue + } + + m.Lock() + defer m.Unlock() + + if handler, found := m.taggedHandlers[tag]; found { + handler.Close() + delete(m.taggedHandlers, tag) + return nil + } + + return core.ErrNoClue +} + func (m *Manager) Start() error { - for _, handler := range m.handlers { + for _, handler := range m.taggedHandlers { + if err := handler.Start(); err != nil { + return err + } + } + + for _, handler := range m.untaggedHandler { if err := handler.Start(); err != nil { return err } @@ -57,7 +89,10 @@ func (m *Manager) Start() error { } func (m *Manager) Close() { - for _, handler := range m.handlers { + for _, handler := range m.taggedHandlers { + handler.Close() + } + for _, handler := range m.untaggedHandler { handler.Close() } } diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index f65ac70e5..bef922d9c 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -105,7 +105,7 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn ctx = proxy.ContextWithTarget(ctx, dest) stream := ray.NewRay(ctx) go handler.Dispatch(ctx, stream) - return ray.NewConnection(stream, zeroAddr, zeroAddr), nil + return ray.NewConnection(stream.InboundOutput(), stream.InboundInput()), nil } newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog() @@ -122,3 +122,7 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn return internet.Dial(ctx, dest) } + +func (h *Handler) GetOutbound() proxy.Outbound { + return h.proxy +} diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 6fb68866d..747ebaa7b 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -73,6 +73,21 @@ func (m *Manager) AddHandler(ctx context.Context, handler core.OutboundHandler) return nil } +func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { + if len(tag) == 0 { + return core.ErrNoClue + } + m.Lock() + defer m.Unlock() + + delete(m.taggedHandler, tag) + if m.defaultHandler.Tag() == tag { + m.defaultHandler = nil + } + + return nil +} + func init() { common.Must(common.RegisterConfig((*proxyman.OutboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*proxyman.OutboundConfig)) diff --git a/dial.go b/dial.go index aef5d8f2a..1819bcafd 100644 --- a/dial.go +++ b/dial.go @@ -16,5 +16,5 @@ func Dial(ctx context.Context, v *Instance, dest net.Destination) (net.Conn, err if err != nil { return nil, err } - return ray.NewConnection(r, &net.TCPAddr{IP: []byte{0, 0, 0, 0}}, &net.TCPAddr{IP: []byte{0, 0, 0, 0}}), nil + return ray.NewConnection(r.InboundOutput(), r.InboundInput()), nil } diff --git a/network.go b/network.go index 7524f7e4c..1d2f50910 100644 --- a/network.go +++ b/network.go @@ -32,6 +32,9 @@ type InboundHandlerManager interface { GetHandler(ctx context.Context, tag string) (InboundHandler, error) // AddHandler adds the given handler into this InboundHandlerManager. AddHandler(ctx context.Context, handler InboundHandler) error + + // RemoveHandler removes a handler from InboundHandlerManager. + RemoveHandler(ctx context.Context, tag string) error } type syncInboundHandlerManager struct { @@ -97,6 +100,9 @@ type OutboundHandlerManager interface { GetDefaultHandler() OutboundHandler // AddHandler adds a handler into this OutboundHandlerManager. AddHandler(ctx context.Context, handler OutboundHandler) error + + // RemoveHandler removes a handler from OutboundHandlerManager. + RemoveHandler(ctx context.Context, tag string) error } type syncOutboundHandlerManager struct { diff --git a/proxy/proxy.go b/proxy/proxy.go index 50162378e..1f7de5b56 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -12,6 +12,7 @@ import ( "v2ray.com/core" "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" "v2ray.com/core/transport/internet" "v2ray.com/core/transport/ray" ) @@ -36,3 +37,20 @@ type Dialer interface { // Dial dials a system connection to the given destination. Dial(ctx context.Context, destination net.Destination) (internet.Connection, error) } + +// UserManager is the interface for Inbounds and Outbounds that can manage their users. +type UserManager interface { + // AddUser adds a new user. + AddUser(context.Context, *protocol.User) error + + // RemoveUser removes an user by email. + RemoveUser(context.Context, string) error +} + +type GetInbound interface { + GetInbound() Inbound +} + +type GetOutbound interface { + GetOutbound() Outbound +} \ No newline at end of file diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 9949383a2..a7b358d27 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -124,6 +124,10 @@ func (h *Handler) AddUser(ctx context.Context, user *protocol.User) error { return h.clients.Add(user) } +func (h *Handler) RemoveUser(ctx context.Context, email string) error { + return newError("not implemented") +} + func transferRequest(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output ray.OutputStream) error { defer output.Close() diff --git a/transport/ray/connection.go b/transport/ray/connection.go index 4a02443ff..2bc4d39b9 100644 --- a/transport/ray/connection.go +++ b/transport/ray/connection.go @@ -6,29 +6,59 @@ import ( "time" "v2ray.com/core/common/buf" + "v2ray.com/core/common/signal" ) -type connection struct { - stream InboundRay - closed bool - localAddr net.Addr - remoteAddr net.Addr +type ConnectionOption func(*connection) - reader *buf.BufferedReader - writer buf.Writer +func ConnLocalAddr(addr net.Addr) ConnectionOption { + return func(c *connection) { + c.localAddr = addr + } } -// NewConnection wraps a Ray into net.Conn. -func NewConnection(stream InboundRay, localAddr net.Addr, remoteAddr net.Addr) net.Conn { - return &connection{ - stream: stream, - localAddr: localAddr, - remoteAddr: remoteAddr, - reader: buf.NewBufferedReader(stream.InboundOutput()), - writer: stream.InboundInput(), +func ConnRemoteAddr(addr net.Addr) ConnectionOption { + return func(c *connection) { + c.remoteAddr = addr } } +func ConnCloseSignal(s *signal.Notifier) ConnectionOption { + return func(c *connection) { + c.closeSignal = s + } +} + +type connection struct { + input InputStream + output OutputStream + closed bool + localAddr net.Addr + remoteAddr net.Addr + closeSignal *signal.Notifier + + reader *buf.BufferedReader +} + +var zeroAddr net.Addr = &net.TCPAddr{IP: []byte{0, 0, 0, 0}} + +// NewConnection wraps a Ray into net.Conn. +func NewConnection(input InputStream, output OutputStream, options ...ConnectionOption) net.Conn { + c := &connection{ + input: input, + output: output, + localAddr: zeroAddr, + remoteAddr: zeroAddr, + reader: buf.NewBufferedReader(input), + } + + for _, opt := range options { + opt(c) + } + + return c +} + // Read implements net.Conn.Read(). func (c *connection) Read(b []byte) (int, error) { if c.closed { @@ -51,7 +81,7 @@ func (c *connection) Write(b []byte) (int, error) { l := len(b) mb := buf.NewMultiBufferCap(l/buf.Size + 1) mb.Write(b) - return l, c.writer.WriteMultiBuffer(mb) + return l, c.output.WriteMultiBuffer(mb) } func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { @@ -59,14 +89,17 @@ func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { return io.ErrClosedPipe } - return c.writer.WriteMultiBuffer(mb) + return c.output.WriteMultiBuffer(mb) } // Close implements net.Conn.Close(). func (c *connection) Close() error { c.closed = true - c.stream.InboundInput().Close() - c.stream.InboundOutput().CloseError() + c.output.Close() + c.input.CloseError() + if c.closeSignal != nil { + c.closeSignal.Signal() + } return nil } diff --git a/v2ray.go b/v2ray.go index 542deef8a..18de4e728 100644 --- a/v2ray.go +++ b/v2ray.go @@ -46,20 +46,18 @@ func New(config *Config) (*Instance, error) { return nil, err } - ctx := context.WithValue(context.Background(), v2rayKey, server) - for _, appSettings := range config.App { settings, err := appSettings.GetInstance() if err != nil { return nil, err } - if _, err := common.CreateObject(ctx, settings); err != nil { + if _, err := server.CreateObject(settings); err != nil { return nil, err } } for _, inbound := range config.Inbound { - rawHandler, err := common.CreateObject(ctx, inbound) + rawHandler, err := server.CreateObject(inbound) if err != nil { return nil, err } @@ -67,13 +65,13 @@ func New(config *Config) (*Instance, error) { if !ok { return nil, newError("not an InboundHandler") } - if err := server.InboundHandlerManager().AddHandler(ctx, handler); err != nil { + if err := server.InboundHandlerManager().AddHandler(context.Background(), handler); err != nil { return nil, err } } for _, outbound := range config.Outbound { - rawHandler, err := common.CreateObject(ctx, outbound) + rawHandler, err := server.CreateObject(outbound) if err != nil { return nil, err } @@ -81,7 +79,7 @@ func New(config *Config) (*Instance, error) { if !ok { return nil, newError("not an OutboundHandler") } - if err := server.OutboundHandlerManager().AddHandler(ctx, handler); err != nil { + if err := server.OutboundHandlerManager().AddHandler(context.Background(), handler); err != nil { return nil, err } } @@ -89,6 +87,11 @@ func New(config *Config) (*Instance, error) { return server, nil } +func (s *Instance) CreateObject(config interface{}) (interface{}, error) { + ctx := context.WithValue(context.Background(), v2rayKey, s) + return common.CreateObject(ctx, config) +} + // ID returns an unique ID for this V2Ray instance. func (s *Instance) ID() uuid.UUID { return s.id From 3a0f211c2284688efd6d1617d57086db8596dc49 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 5 Feb 2018 23:39:04 +0100 Subject: [PATCH 28/63] gofmt --- app/commander/errors.generated.go | 4 +++- app/commander/outbound.go | 2 +- app/proxyman/command/errors.generated.go | 4 +++- proxy/proxy.go | 2 +- proxy/vmess/vmess.go | 18 +++++++++--------- transport/internet/kcp/sending.go | 2 +- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/commander/errors.generated.go b/app/commander/errors.generated.go index fd7b91797..70edc7084 100644 --- a/app/commander/errors.generated.go +++ b/app/commander/errors.generated.go @@ -2,4 +2,6 @@ package commander import "v2ray.com/core/common/errors" -func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Commander") } +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).Path("App", "Commander") +} diff --git a/app/commander/outbound.go b/app/commander/outbound.go index 1338c7451..018cef242 100644 --- a/app/commander/outbound.go +++ b/app/commander/outbound.go @@ -62,4 +62,4 @@ func (co *CommanderOutbound) Start() error { return nil } -func (co *CommanderOutbound) Close() {} \ No newline at end of file +func (co *CommanderOutbound) Close() {} diff --git a/app/proxyman/command/errors.generated.go b/app/proxyman/command/errors.generated.go index 3cbe9949a..7c196cf5d 100644 --- a/app/proxyman/command/errors.generated.go +++ b/app/proxyman/command/errors.generated.go @@ -2,4 +2,6 @@ package command import "v2ray.com/core/common/errors" -func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Proxyman", "Command") } +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).Path("App", "Proxyman", "Command") +} diff --git a/proxy/proxy.go b/proxy/proxy.go index 1f7de5b56..7174c2453 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -53,4 +53,4 @@ type GetInbound interface { type GetOutbound interface { GetOutbound() Outbound -} \ No newline at end of file +} diff --git a/proxy/vmess/vmess.go b/proxy/vmess/vmess.go index 0fbefc43c..9c805fa23 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -22,9 +22,9 @@ const ( ) type idEntry struct { - id *protocol.ID - userIdx int - lastSec protocol.Timestamp + id *protocol.ID + userIdx int + lastSec protocol.Timestamp } type TimedUserValidator struct { @@ -114,17 +114,17 @@ func (v *TimedUserValidator) Add(user *protocol.User) error { nowSec := time.Now().Unix() entry := &idEntry{ - id: account.ID, - userIdx: idx, - lastSec: protocol.Timestamp(nowSec - cacheDurationSec), + id: account.ID, + userIdx: idx, + lastSec: protocol.Timestamp(nowSec - cacheDurationSec), } v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry) v.ids = append(v.ids, entry) for _, alterid := range account.AlterIDs { entry := &idEntry{ - id: alterid, - userIdx: idx, - lastSec: protocol.Timestamp(nowSec - cacheDurationSec), + id: alterid, + userIdx: idx, + lastSec: protocol.Timestamp(nowSec - cacheDurationSec), } v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry) v.ids = append(v.ids, entry) diff --git a/transport/internet/kcp/sending.go b/transport/internet/kcp/sending.go index 3fcf3ceb7..0bc02c8d8 100644 --- a/transport/internet/kcp/sending.go +++ b/transport/internet/kcp/sending.go @@ -364,7 +364,7 @@ func (w *SendingWorker) CloseWrite() { } func (w *SendingWorker) IsEmpty() bool { - w.RLock() + w.RLock() defer w.RUnlock() return w.window.IsEmpty() From ede2c3996732eaeea82e3182102a330f03b0c74b Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 6 Feb 2018 11:16:49 +0100 Subject: [PATCH 29/63] non-blocking timer. Fixes #848 --- common/signal/timer.go | 12 ++++++++++-- common/signal/timer_test.go | 12 ++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/common/signal/timer.go b/common/signal/timer.go index 164821015..30989b2f4 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -12,6 +12,7 @@ type ActivityUpdater interface { type ActivityTimer struct { updated chan bool timeout chan time.Duration + closing chan bool } func (t *ActivityTimer) Update() { @@ -22,11 +23,17 @@ func (t *ActivityTimer) Update() { } func (t *ActivityTimer) SetTimeout(timeout time.Duration) { - t.timeout <- timeout + select { + case <-t.closing: + case t.timeout <- timeout: + } } func (t *ActivityTimer) run(ctx context.Context, cancel context.CancelFunc) { - defer cancel() + defer func() { + cancel() + close(t.closing) + }() timeout := <-t.timeout if timeout == 0 { @@ -66,6 +73,7 @@ func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeo timer := &ActivityTimer{ timeout: make(chan time.Duration, 1), updated: make(chan bool, 1), + closing: make(chan bool), } timer.timeout <- timeout go timer.run(ctx, cancel) diff --git a/common/signal/timer_test.go b/common/signal/timer_test.go index a34cfbc19..f299b308b 100644 --- a/common/signal/timer_test.go +++ b/common/signal/timer_test.go @@ -32,3 +32,15 @@ func TestActivityTimerUpdate(t *testing.T) { assert(ctx.Err(), IsNotNil) runtime.KeepAlive(timer) } + +func TestActivityTimerNonBlocking(t *testing.T) { + assert := With(t) + + ctx, cancel := context.WithCancel(context.Background()) + timer := CancelAfterInactivity(ctx, cancel, 0) + time.Sleep(time.Second * 1) + assert(ctx, HasDone) + timer.SetTimeout(0) + timer.SetTimeout(1) + timer.SetTimeout(2) +} From 01c76d70da2efb40323a9012051e75589d5ce201 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 6 Feb 2018 12:22:46 +0100 Subject: [PATCH 30/63] listen on localhost only --- testing/scenarios/common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/scenarios/common.go b/testing/scenarios/common.go index ebfc663d3..ac6155bb8 100644 --- a/testing/scenarios/common.go +++ b/testing/scenarios/common.go @@ -23,7 +23,7 @@ import ( ) func pickPort() net.Port { - listener, err := net.Listen("tcp4", ":0") + listener, err := net.Listen("tcp4", "127.0.0.1:0") common.Must(err) defer listener.Close() From 2328c69fbaa89b6fcb8a4bf81eda7195971f869e Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 6 Feb 2018 13:47:36 +0100 Subject: [PATCH 31/63] include new packages --- main/distro/all/all.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 1b55c1cad..21a907160 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -2,10 +2,12 @@ package all import ( // The following are necessary as they register handlers in their init functions. + _ "v2ray.com/core/app/commander" _ "v2ray.com/core/app/dispatcher" _ "v2ray.com/core/app/dns" _ "v2ray.com/core/app/log" _ "v2ray.com/core/app/policy" + _ "v2ray.com/core/app/proxyman/command" _ "v2ray.com/core/app/proxyman/inbound" _ "v2ray.com/core/app/proxyman/outbound" _ "v2ray.com/core/app/router" From deaee9fa6585d9bd25fed4dbd73b8cf5eeafebe1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 6 Feb 2018 13:47:50 +0100 Subject: [PATCH 32/63] test case for removing handler on the fly --- app/commander/commander.go | 28 ++++++- app/proxyman/outbound/outbound.go | 7 +- testing/scenarios/command_test.go | 126 ++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 testing/scenarios/command_test.go diff --git a/app/commander/commander.go b/app/commander/commander.go index 43a4aa929..2afc7a75c 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -9,6 +9,7 @@ import ( "google.golang.org/grpc" "v2ray.com/core" + "v2ray.com/core/common" ) type Commander struct { @@ -19,6 +20,21 @@ type Commander struct { callbacks []core.ServiceRegistryCallback } +func NewCommander(ctx context.Context, config *Config) (*Commander, error) { + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context.") + } + c := &Commander{ + config: *config, + ohm: v.OutboundHandlerManager(), + } + if err := v.RegisterFeature((*core.Commander)(nil), c); err != nil { + return nil, err + } + return c, nil +} + func (c *Commander) RegisterService(callback core.ServiceRegistryCallback) { c.Lock() defer c.Unlock() @@ -42,7 +58,11 @@ func (c *Commander) Start() error { buffer: make(chan net.Conn, 4), } - c.server.Serve(listener) + go func() { + if err := c.server.Serve(listener); err != nil { + newError("failed to start grpc server").Base(err).AtError().WriteToLog() + } + }() c.ohm.RemoveHandler(context.Background(), c.config.Tag) c.ohm.AddHandler(context.Background(), &CommanderOutbound{ @@ -61,3 +81,9 @@ func (c *Commander) Close() { c.server = nil } } + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { + return NewCommander(ctx, cfg.(*Config)) + })) +} diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 747ebaa7b..cc26c48a2 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -14,8 +14,9 @@ import ( // Manager is to manage all outbound handlers. type Manager struct { sync.RWMutex - defaultHandler core.OutboundHandler - taggedHandler map[string]core.OutboundHandler + defaultHandler core.OutboundHandler + taggedHandler map[string]core.OutboundHandler + untaggedHandlers []core.OutboundHandler } // New creates a new Manager. @@ -68,6 +69,8 @@ func (m *Manager) AddHandler(ctx context.Context, handler core.OutboundHandler) tag := handler.Tag() if len(tag) > 0 { m.taggedHandler[tag] = handler + } else { + m.untaggedHandlers = append(m.untaggedHandlers, handler) } return nil diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go new file mode 100644 index 000000000..000956bca --- /dev/null +++ b/testing/scenarios/command_test.go @@ -0,0 +1,126 @@ +package scenarios + +import ( + "context" + "fmt" + "testing" + + "google.golang.org/grpc" + "v2ray.com/core" + "v2ray.com/core/app/commander" + "v2ray.com/core/app/proxyman" + "v2ray.com/core/app/proxyman/command" + "v2ray.com/core/app/router" + "v2ray.com/core/common/net" + "v2ray.com/core/common/serial" + "v2ray.com/core/proxy/dokodemo" + "v2ray.com/core/proxy/freedom" + "v2ray.com/core/testing/servers/tcp" + . "v2ray.com/ext/assert" +) + +func TestCommanderRemoveHandler(t *testing.T) { + assert := With(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert(err, IsNil) + defer tcpServer.Close() + + clientPort := pickPort() + cmdPort := pickPort() + clientConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&commander.Config{Tag: "api"}), + serial.ToTypedMessage(&router.Config{ + Rule: []*router.RoutingRule{ + { + InboundTag: []string{"api"}, + Tag: "api", + }, + }, + }), + serial.ToTypedMessage(&command.Config{}), + }, + Inbound: []*core.InboundHandlerConfig{ + { + Tag: "d", + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(clientPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + { + Tag: "api", + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(cmdPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + Tag: "default-outbound", + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + servers, err := InitializeServerConfigs(clientConfig) + assert(err, IsNil) + + { + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert(err, IsNil) + + payload := "commander request." + nBytes, err := conn.Write([]byte(payload)) + assert(err, IsNil) + assert(nBytes, Equals, len(payload)) + + response := make([]byte, 1024) + nBytes, err = conn.Read(response) + assert(err, IsNil) + assert(response[:nBytes], Equals, xor([]byte(payload))) + assert(conn.Close(), IsNil) + } + + cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure()) + assert(err, IsNil) + + hsClient := command.NewHandlerServiceClient(cmdConn) + resp, err := hsClient.RemoveInbound(context.Background(), &command.RemoveInboundRequest{ + Tag: "d", + }) + assert(err, IsNil) + assert(resp, IsNotNil) + + { + _, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert(err, IsNotNil) + } + + CloseAllServers(servers) +} From 8b5fe1a13b934a9c2d42c83ac6010321b9fa8e66 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 7 Feb 2018 12:34:15 +0100 Subject: [PATCH 33/63] correct handler running status --- app/proxyman/inbound/inbound.go | 30 +++++++++++++++++++++++------- app/proxyman/outbound/outbound.go | 24 +++++++++++++----------- v2ray.go | 17 +++++++++++++++++ 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index 2cf37dedb..ee1508bcd 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -13,9 +13,10 @@ import ( // Manager is to manage all inbound handlers. type Manager struct { - sync.RWMutex + access sync.RWMutex untaggedHandler []core.InboundHandler taggedHandlers map[string]core.InboundHandler + running bool } func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) { @@ -33,8 +34,8 @@ func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) } func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) error { - m.Lock() - defer m.Unlock() + m.access.Lock() + defer m.access.Unlock() tag := handler.Tag() if len(tag) > 0 { @@ -42,12 +43,17 @@ func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) e } else { m.untaggedHandler = append(m.untaggedHandler, handler) } + + if m.running { + return handler.Start() + } + return nil } func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) { - m.RLock() - defer m.RUnlock() + m.access.RLock() + defer m.access.RUnlock() handler, found := m.taggedHandlers[tag] if !found { @@ -61,8 +67,8 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { return core.ErrNoClue } - m.Lock() - defer m.Unlock() + m.access.Lock() + defer m.access.Unlock() if handler, found := m.taggedHandlers[tag]; found { handler.Close() @@ -74,6 +80,11 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { } func (m *Manager) Start() error { + m.access.Lock() + defer m.access.Unlock() + + m.running = true + for _, handler := range m.taggedHandlers { if err := handler.Start(); err != nil { return err @@ -89,6 +100,11 @@ func (m *Manager) Start() error { } func (m *Manager) Close() { + m.access.Lock() + defer m.access.Unlock() + + m.running = false + for _, handler := range m.taggedHandlers { handler.Close() } diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index cc26c48a2..8946b411c 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -13,10 +13,11 @@ import ( // Manager is to manage all outbound handlers. type Manager struct { - sync.RWMutex + access sync.RWMutex defaultHandler core.OutboundHandler taggedHandler map[string]core.OutboundHandler untaggedHandlers []core.OutboundHandler + running bool } // New creates a new Manager. @@ -34,15 +35,16 @@ func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) return m, nil } -// Start implements Application.Start +// Start implements core.Feature func (*Manager) Start() error { return nil } -// Close implements Application.Close +// Close implements core.Feature func (*Manager) Close() {} func (m *Manager) GetDefaultHandler() core.OutboundHandler { - m.RLock() - defer m.RUnlock() + m.access.RLock() + defer m.access.RUnlock() + if m.defaultHandler == nil { return nil } @@ -50,8 +52,8 @@ func (m *Manager) GetDefaultHandler() core.OutboundHandler { } func (m *Manager) GetHandler(tag string) core.OutboundHandler { - m.RLock() - defer m.RUnlock() + m.access.RLock() + defer m.access.RUnlock() if handler, found := m.taggedHandler[tag]; found { return handler } @@ -59,8 +61,8 @@ func (m *Manager) GetHandler(tag string) core.OutboundHandler { } func (m *Manager) AddHandler(ctx context.Context, handler core.OutboundHandler) error { - m.Lock() - defer m.Unlock() + m.access.Lock() + defer m.access.Unlock() if m.defaultHandler == nil { m.defaultHandler = handler @@ -80,8 +82,8 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { if len(tag) == 0 { return core.ErrNoClue } - m.Lock() - defer m.Unlock() + m.access.Lock() + defer m.access.Unlock() delete(m.taggedHandler, tag) if m.defaultHandler.Tag() == tag { diff --git a/v2ray.go b/v2ray.go index 18de4e728..7bca7babc 100644 --- a/v2ray.go +++ b/v2ray.go @@ -2,6 +2,7 @@ package core import ( "context" + "sync" "v2ray.com/core/common" "v2ray.com/core/common/uuid" @@ -30,8 +31,10 @@ type Instance struct { clock syncClock cmd syncCommander + access sync.Mutex features []Feature id uuid.UUID + running bool } // New returns a new V2Ray instance based on given configuration. @@ -99,6 +102,10 @@ func (s *Instance) ID() uuid.UUID { // Close shutdown the V2Ray instance. func (s *Instance) Close() { + s.access.Lock() + defer s.access.Unlock() + + s.running = false for _, f := range s.features { f.Close() } @@ -106,6 +113,10 @@ func (s *Instance) Close() { // Start starts the V2Ray instance, including all registered features. When Start returns error, the state of the instance is unknown. func (s *Instance) Start() error { + s.access.Lock() + defer s.access.Unlock() + + s.running = true for _, f := range s.features { if err := f.Start(); err != nil { return err @@ -139,7 +150,13 @@ func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error case Commander, *Commander: s.cmd.Set(instance.(Commander)) } + s.access.Lock() + defer s.access.Unlock() + s.features = append(s.features, instance) + if s.running { + return instance.Start() + } return nil } From 8b83bf2283303b2e40f11bdf207c8eb0ebda69fe Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 7 Feb 2018 12:38:12 +0100 Subject: [PATCH 34/63] comments and test cases. --- app/proxyman/outbound/handler.go | 1 + app/proxyman/outbound/handler_test.go | 1 + app/proxyman/outbound/outbound.go | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index bef922d9c..c792994eb 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -67,6 +67,7 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (*Handl return h, nil } +// Tag implements core.OutboundHandler. func (h *Handler) Tag() string { return h.config.Tag } diff --git a/app/proxyman/outbound/handler_test.go b/app/proxyman/outbound/handler_test.go index 109b087f0..bd15e0349 100644 --- a/app/proxyman/outbound/handler_test.go +++ b/app/proxyman/outbound/handler_test.go @@ -12,4 +12,5 @@ func TestInterfaces(t *testing.T) { assert := With(t) assert((*Handler)(nil), Implements, (*core.OutboundHandler)(nil)) + assert((*Manager)(nil), Implements, (*core.OutboundHandlerManager)(nil)) } diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 8946b411c..ccd988455 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -41,6 +41,7 @@ func (*Manager) Start() error { return nil } // Close implements core.Feature func (*Manager) Close() {} +// GetDefaultHandler implements core.OutboundHandlerManager. func (m *Manager) GetDefaultHandler() core.OutboundHandler { m.access.RLock() defer m.access.RUnlock() @@ -51,6 +52,7 @@ func (m *Manager) GetDefaultHandler() core.OutboundHandler { return m.defaultHandler } +// GetHandler implements core.OutboundHandlerManager. func (m *Manager) GetHandler(tag string) core.OutboundHandler { m.access.RLock() defer m.access.RUnlock() @@ -60,6 +62,7 @@ func (m *Manager) GetHandler(tag string) core.OutboundHandler { return nil } +// AddHandler implements core.OutboundHandlerManager. func (m *Manager) AddHandler(ctx context.Context, handler core.OutboundHandler) error { m.access.Lock() defer m.access.Unlock() @@ -78,6 +81,7 @@ func (m *Manager) AddHandler(ctx context.Context, handler core.OutboundHandler) return nil } +// RemoveHandler implements core.OutboundHandlerManager. func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { if len(tag) == 0 { return core.ErrNoClue From a1ae4aa5156c926a0a2467367683d63a6a8e81a1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 7 Feb 2018 16:35:22 +0100 Subject: [PATCH 35/63] cleanup session history --- proxy/vmess/encoding/encoding_test.go | 2 +- proxy/vmess/encoding/server.go | 46 ++++++++++----------------- proxy/vmess/inbound/inbound.go | 2 +- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/proxy/vmess/encoding/encoding_test.go b/proxy/vmess/encoding/encoding_test.go index 8d0211e48..ee6b1a1d6 100644 --- a/proxy/vmess/encoding/encoding_test.go +++ b/proxy/vmess/encoding/encoding_test.go @@ -46,7 +46,7 @@ func TestRequestSerialization(t *testing.T) { buffer2.Append(buffer.Bytes()) ctx, cancel := context.WithCancel(context.Background()) - sessionHistory := NewSessionHistory(ctx) + sessionHistory := NewSessionHistory() userValidator := vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash) userValidator.Add(user) diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index 0859ed822..8a81a33a9 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -1,7 +1,6 @@ package encoding import ( - "context" "crypto/aes" "crypto/cipher" "crypto/md5" @@ -32,26 +31,25 @@ type SessionHistory struct { sync.RWMutex cache map[sessionId]time.Time token *signal.Semaphore - ctx context.Context + timer *time.Timer } -func NewSessionHistory(ctx context.Context) *SessionHistory { +func NewSessionHistory() *SessionHistory { h := &SessionHistory{ cache: make(map[sessionId]time.Time, 128), token: signal.NewSemaphore(1), - ctx: ctx, } return h } func (h *SessionHistory) add(session sessionId) { h.Lock() - h.cache[session] = time.Now().Add(time.Minute * 3) - h.Unlock() + defer h.Unlock() + h.cache[session] = time.Now().Add(time.Minute * 3) select { case <-h.token.Wait(): - go h.run() + h.timer = time.AfterFunc(time.Minute*3, h.removeExpiredEntries) default: } } @@ -66,31 +64,21 @@ func (h *SessionHistory) has(session sessionId) bool { return false } -func (h *SessionHistory) run() { - defer h.token.Signal() +func (h *SessionHistory) removeExpiredEntries() { + now := time.Now() - for { - select { - case <-h.ctx.Done(): - return - case <-time.After(time.Second * 30): - } - session2Remove := make([]sessionId, 0, 16) - now := time.Now() - h.Lock() - if len(h.cache) == 0 { - h.Unlock() - return - } - for session, expire := range h.cache { - if expire.Before(now) { - session2Remove = append(session2Remove, session) - } - } - for _, session := range session2Remove { + h.Lock() + defer h.Unlock() + + for session, expire := range h.cache { + if expire.Before(now) { delete(h.cache, session) } - h.Unlock() + } + + if h.timer != nil { + h.timer.Stop() + h.timer = nil } } diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index a7b358d27..7490cbc74 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -93,7 +93,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) { clients: vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash), detours: config.Detour, usersByEmail: newUserByEmail(config.User, config.GetDefaultValue()), - sessionHistory: encoding.NewSessionHistory(ctx), + sessionHistory: encoding.NewSessionHistory(), } for _, user := range config.User { From efcb56727353626cc5e373931bf89f1a8d98d524 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 15:39:46 +0100 Subject: [PATCH 36/63] remove context in struct --- app/commander/commander.go | 4 +- app/commander/outbound.go | 25 ++++++- app/dispatcher/default.go | 2 +- app/dns/server.go | 3 +- app/log/log.go | 4 +- app/policy/manager.go | 3 +- app/proxyman/command/command.go | 4 +- app/proxyman/inbound/always.go | 3 +- app/proxyman/inbound/dynamic.go | 53 +++++++-------- app/proxyman/inbound/inbound.go | 4 +- app/proxyman/inbound/worker.go | 82 +++++++++-------------- app/proxyman/mux/mux.go | 3 +- app/proxyman/mux/session.go | 8 ++- app/proxyman/mux/writer.go | 3 +- app/proxyman/outbound/outbound.go | 2 +- app/router/router.go | 4 +- clock.go | 6 +- commander.go | 6 +- common/interfaces.go | 17 ++++- common/signal/done.go | 48 +++++++++++++ common/signal/notifier.go | 8 +-- common/signal/semaphore.go | 10 +-- common/signal/task.go | 60 +++++++++++++++++ common/signal/timer.go | 10 +-- dns.go | 14 ++-- network.go | 13 ++-- policy.go | 8 +-- proxy/vmess/encoding/encoding_test.go | 3 +- proxy/vmess/encoding/server.go | 27 ++++---- proxy/vmess/inbound/inbound.go | 10 ++- proxy/vmess/vmess.go | 56 +++++++++------- router.go | 13 ++-- testing/servers/http/http.go | 4 +- testing/servers/tcp/tcp.go | 4 +- testing/servers/udp/udp.go | 4 +- transport/internet/kcp/listener.go | 29 ++------ transport/internet/tcp/hub.go | 32 +++------ transport/internet/tcp_hub.go | 28 ++------ transport/internet/udp/dispatcher_test.go | 4 +- transport/internet/udp/hub.go | 6 +- transport/internet/websocket/hub.go | 8 +-- transport/ray/direct.go | 3 +- transport/ray/ray.go | 7 +- v2ray.go | 4 +- 44 files changed, 379 insertions(+), 270 deletions(-) create mode 100644 common/signal/done.go create mode 100644 common/signal/task.go diff --git a/app/commander/commander.go b/app/commander/commander.go index 2afc7a75c..e63935ff3 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -72,7 +72,7 @@ func (c *Commander) Start() error { return nil } -func (c *Commander) Close() { +func (c *Commander) Close() error { c.Lock() defer c.Unlock() @@ -80,6 +80,8 @@ func (c *Commander) Close() { c.server.Stop() c.server = nil } + + return nil } func init() { diff --git a/app/commander/outbound.go b/app/commander/outbound.go index 018cef242..9d9f36a57 100644 --- a/app/commander/outbound.go +++ b/app/commander/outbound.go @@ -3,6 +3,7 @@ package commander import ( "context" "net" + "sync" "v2ray.com/core/common/signal" "v2ray.com/core/transport/ray" @@ -43,12 +44,24 @@ func (l *OutboundListener) Addr() net.Addr { type CommanderOutbound struct { tag string listener *OutboundListener + access sync.RWMutex + closed bool } func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) { + co.access.RLock() + + if co.closed { + r.OutboundInput().CloseError() + r.OutboundOutput().CloseError() + co.access.RUnlock() + return + } + closeSignal := signal.NewNotifier() c := ray.NewConnection(r.OutboundInput(), r.OutboundOutput(), ray.ConnCloseSignal(closeSignal)) co.listener.add(c) + co.access.RUnlock() <-closeSignal.Wait() return @@ -59,7 +72,17 @@ func (co *CommanderOutbound) Tag() string { } func (co *CommanderOutbound) Start() error { + co.access.Lock() + co.closed = false + co.access.Unlock() return nil } -func (co *CommanderOutbound) Close() {} +func (co *CommanderOutbound) Close() error { + co.access.Lock() + co.closed = true + co.listener.Close() + co.access.Unlock() + + return nil +} diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 5f103a1f1..6be84fcf3 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -49,7 +49,7 @@ func (*DefaultDispatcher) Start() error { } // Close implements app.Application. -func (*DefaultDispatcher) Close() {} +func (*DefaultDispatcher) Close() error { return nil } // Dispatch implements core.Dispatcher. func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) { diff --git a/app/dns/server.go b/app/dns/server.go index c26c7985c..ba273b53d 100644 --- a/app/dns/server.go +++ b/app/dns/server.go @@ -83,7 +83,8 @@ func (s *Server) Start() error { return nil } -func (*Server) Close() { +func (*Server) Close() error { + return nil } func (s *Server) GetCached(domain string) []net.IP { diff --git a/app/log/log.go b/app/log/log.go index 5614da2b1..c2dc5e579 100644 --- a/app/log/log.go +++ b/app/log/log.go @@ -103,11 +103,13 @@ func (g *Instance) Handle(msg log.Message) { } // Close implement app.Application.Close(). -func (g *Instance) Close() { +func (g *Instance) Close() error { g.Lock() defer g.Unlock() g.active = false + + return nil } func init() { diff --git a/app/policy/manager.go b/app/policy/manager.go index d21eec4f2..14f7b7b9b 100644 --- a/app/policy/manager.go +++ b/app/policy/manager.go @@ -51,7 +51,8 @@ func (m *Instance) Start() error { } // Close implements app.Application.Close(). -func (m *Instance) Close() { +func (m *Instance) Close() error { + return nil } func init() { diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go index 7ee973c4f..4d93adcad 100644 --- a/app/proxyman/command/command.go +++ b/app/proxyman/command/command.go @@ -119,7 +119,9 @@ func (*feature) Start() error { return nil } -func (*feature) Close() {} +func (*feature) Close() error { + return nil +} func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { diff --git a/app/proxyman/inbound/always.go b/app/proxyman/inbound/always.go index ed0855743..7253fa5e1 100644 --- a/app/proxyman/inbound/always.go +++ b/app/proxyman/inbound/always.go @@ -76,10 +76,11 @@ func (h *AlwaysOnInboundHandler) Start() error { return nil } -func (h *AlwaysOnInboundHandler) Close() { +func (h *AlwaysOnInboundHandler) Close() error { for _, worker := range h.workers { worker.Close() } + return nil } func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) { diff --git a/app/proxyman/inbound/dynamic.go b/app/proxyman/inbound/dynamic.go index 281f8eb63..e6f77972b 100644 --- a/app/proxyman/inbound/dynamic.go +++ b/app/proxyman/inbound/dynamic.go @@ -5,17 +5,18 @@ import ( "sync" "time" + "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/app/proxyman/mux" "v2ray.com/core/common/dice" "v2ray.com/core/common/net" + "v2ray.com/core/common/signal" "v2ray.com/core/proxy" ) type DynamicInboundHandler struct { tag string - ctx context.Context - cancel context.CancelFunc + v *core.Instance proxyConfig interface{} receiverConfig *proxyman.ReceiverConfig portMutex sync.Mutex @@ -24,18 +25,26 @@ type DynamicInboundHandler struct { worker []worker lastRefresh time.Time mux *mux.Server + task *signal.PeriodicTask } func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) { - ctx, cancel := context.WithCancel(ctx) + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context.") + } h := &DynamicInboundHandler{ - ctx: ctx, tag: tag, - cancel: cancel, proxyConfig: proxyConfig, receiverConfig: receiverConfig, portsInUse: make(map[net.Port]bool), mux: mux.NewServer(ctx), + v: v, + } + + h.task = &signal.PeriodicTask{ + Interval: time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()), + Execute: h.refresh, } return h, nil @@ -59,9 +68,7 @@ func (h *DynamicInboundHandler) allocatePort() net.Port { } } -func (h *DynamicInboundHandler) waitAnyCloseWorkers(ctx context.Context, cancel context.CancelFunc, workers []worker, duration time.Duration) { - time.Sleep(duration) - cancel() +func (h *DynamicInboundHandler) closeWorkers(workers []worker) { ports2Del := make([]net.Port, len(workers)) for idx, worker := range workers { ports2Del[idx] = worker.Port() @@ -80,7 +87,6 @@ func (h *DynamicInboundHandler) refresh() error { timeout := time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()) * 2 concurrency := h.receiverConfig.AllocationStrategy.GetConcurrencyValue() - ctx, cancel := context.WithTimeout(h.ctx, timeout) workers := make([]worker, 0, concurrency) address := h.receiverConfig.Listen.AsAddress() @@ -89,11 +95,12 @@ func (h *DynamicInboundHandler) refresh() error { } for i := uint32(0); i < concurrency; i++ { port := h.allocatePort() - p, err := proxy.CreateInboundHandler(ctx, h.proxyConfig) + rawProxy, err := h.v.CreateObject(h.proxyConfig) if err != nil { newError("failed to create proxy instance").Base(err).AtWarning().WriteToLog() continue } + p := rawProxy.(proxy.Inbound) nl := p.Network() if nl.HasNetwork(net.Network_TCP) { worker := &tcpWorker{ @@ -134,33 +141,19 @@ func (h *DynamicInboundHandler) refresh() error { h.worker = workers h.workerMutex.Unlock() - go h.waitAnyCloseWorkers(ctx, cancel, workers, timeout) + time.AfterFunc(timeout, func() { + h.closeWorkers(workers) + }) return nil } -func (h *DynamicInboundHandler) monitor() { - timer := time.NewTicker(time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue())) - defer timer.Stop() - - for { - select { - case <-h.ctx.Done(): - return - case <-timer.C: - h.refresh() - } - } -} - func (h *DynamicInboundHandler) Start() error { - err := h.refresh() - go h.monitor() - return err + return h.task.Start() } -func (h *DynamicInboundHandler) Close() { - h.cancel() +func (h *DynamicInboundHandler) Close() error { + return h.task.Close() } func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) { diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index ee1508bcd..cfd4cd5c1 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -99,7 +99,7 @@ func (m *Manager) Start() error { return nil } -func (m *Manager) Close() { +func (m *Manager) Close() error { m.access.Lock() defer m.access.Unlock() @@ -111,6 +111,8 @@ func (m *Manager) Close() { for _, handler := range m.untaggedHandler { handler.Close() } + + return nil } func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (core.InboundHandler, error) { diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 51bdc6ff7..95cd882c1 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -9,8 +9,10 @@ import ( "v2ray.com/core" "v2ray.com/core/app/proxyman" + "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" + "v2ray.com/core/common/signal" "v2ray.com/core/proxy" "v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet/tcp" @@ -19,7 +21,7 @@ import ( type worker interface { Start() error - Close() + Close() error Port() net.Port Proxy() proxy.Inbound } @@ -34,13 +36,11 @@ type tcpWorker struct { dispatcher core.Dispatcher sniffers []proxyman.KnownProtocols - ctx context.Context - cancel context.CancelFunc - hub internet.Listener + hub internet.Listener } func (w *tcpWorker) callback(conn internet.Connection) { - ctx, cancel := context.WithCancel(w.ctx) + ctx, cancel := context.WithCancel(context.Background()) if w.recvOrigDest { dest, err := tcp.GetOriginalDestination(conn) if err != nil { @@ -70,45 +70,24 @@ func (w *tcpWorker) Proxy() proxy.Inbound { } func (w *tcpWorker) Start() error { - ctx, cancel := context.WithCancel(context.Background()) - w.ctx = ctx - w.cancel = cancel - ctx = internet.ContextWithStreamSettings(ctx, w.stream) - conns := make(chan internet.Connection, 16) - hub, err := internet.ListenTCP(ctx, w.address, w.port, conns) + ctx := internet.ContextWithStreamSettings(context.Background(), w.stream) + hub, err := internet.ListenTCP(ctx, w.address, w.port, func(conn internet.Connection) { + go w.callback(conn) + }) if err != nil { return newError("failed to listen TCP on ", w.port).AtWarning().Base(err) } - go w.handleConnections(conns) w.hub = hub return nil } -func (w *tcpWorker) handleConnections(conns <-chan internet.Connection) { - for { - select { - case <-w.ctx.Done(): - w.hub.Close() - L: - for { - select { - case conn := <-conns: - conn.Close() - default: - break L - } - } - return - case conn := <-conns: - go w.callback(conn) - } - } -} - -func (w *tcpWorker) Close() { +func (w *tcpWorker) Close() error { if w.hub != nil { - w.cancel() + common.Close(w.hub) + common.Close(w.proxy) } + + return nil } func (w *tcpWorker) Port() net.Port { @@ -121,8 +100,7 @@ type udpConn struct { output func([]byte) (int, error) remote net.Addr local net.Addr - ctx context.Context - cancel context.CancelFunc + done *signal.Done } func (c *udpConn) updateActivity() { @@ -135,7 +113,7 @@ func (c *udpConn) Read(buf []byte) (int, error) { defer in.Release() c.updateActivity() return copy(buf, in.Bytes()), nil - case <-c.ctx.Done(): + case <-c.done.C(): return 0, io.EOF } } @@ -150,6 +128,7 @@ func (c *udpConn) Write(buf []byte) (int, error) { } func (c *udpConn) Close() error { + common.Close(c.done) return nil } @@ -189,8 +168,7 @@ type udpWorker struct { tag string dispatcher core.Dispatcher - ctx context.Context - cancel context.CancelFunc + done *signal.Done activeConn map[connId]*udpConn } @@ -215,6 +193,7 @@ func (w *udpWorker) getConnection(id connId) (*udpConn, bool) { IP: w.address.IP(), Port: int(w.port), }, + done: signal.NewDone(), } w.activeConn[id] = conn @@ -230,16 +209,15 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest conn, existing := w.getConnection(id) select { case conn.input <- b: + case <-conn.done.C(): + b.Release() default: b.Release() } if !existing { go func() { - ctx := w.ctx - ctx, cancel := context.WithCancel(ctx) - conn.ctx = ctx - conn.cancel = cancel + ctx := context.Background() if originalDest.IsValid() { ctx = proxy.ContextWithOriginalTarget(ctx, originalDest) } @@ -251,8 +229,8 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil { newError("connection ends").Base(err).WriteToLog() } + conn.Close() w.removeConn(id) - cancel() }() } } @@ -265,9 +243,7 @@ func (w *udpWorker) removeConn(id connId) { func (w *udpWorker) Start() error { w.activeConn = make(map[connId]*udpConn, 16) - ctx, cancel := context.WithCancel(context.Background()) - w.ctx = ctx - w.cancel = cancel + w.done = signal.NewDone() h, err := udp.ListenUDP(w.address, w.port, udp.ListenOption{ Callback: w.callback, ReceiveOriginalDest: w.recvOrigDest, @@ -280,11 +256,13 @@ func (w *udpWorker) Start() error { return nil } -func (w *udpWorker) Close() { +func (w *udpWorker) Close() error { if w.hub != nil { w.hub.Close() - w.cancel() + w.done.Close() + common.Close(w.proxy) } + return nil } func (w *udpWorker) monitor() { @@ -293,7 +271,7 @@ func (w *udpWorker) monitor() { for { select { - case <-w.ctx.Done(): + case <-w.done.C(): return case <-timer.C: nowSec := time.Now().Unix() @@ -301,7 +279,7 @@ func (w *udpWorker) monitor() { for addr, conn := range w.activeConn { if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 8 { delete(w.activeConn, addr) - conn.cancel() + conn.Close() } } w.Unlock() diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index 52b2b8851..e2ae68fbc 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -291,7 +291,8 @@ func (s *Server) Start() error { return nil } -func (s *Server) Close() { +func (s *Server) Close() error { + return nil } type ServerWorker struct { diff --git a/app/proxyman/mux/session.go b/app/proxyman/mux/session.go index 66e1b7290..c828d21c8 100644 --- a/app/proxyman/mux/session.go +++ b/app/proxyman/mux/session.go @@ -103,12 +103,12 @@ func (m *SessionManager) CloseIfNoSession() bool { return true } -func (m *SessionManager) Close() { +func (m *SessionManager) Close() error { m.Lock() defer m.Unlock() if m.closed { - return + return nil } m.closed = true @@ -119,6 +119,7 @@ func (m *SessionManager) Close() { } m.sessions = nil + return nil } // Session represents a client connection in a Mux connection. @@ -131,10 +132,11 @@ type Session struct { } // Close closes all resources associated with this session. -func (s *Session) Close() { +func (s *Session) Close() error { s.output.Close() s.input.Close() s.parent.Remove(s.ID) + return nil } // NewReader creates a buf.Reader based on the transfer type of this Session. diff --git a/app/proxyman/mux/writer.go b/app/proxyman/mux/writer.go index 64c290bdf..f8cbc21f2 100644 --- a/app/proxyman/mux/writer.go +++ b/app/proxyman/mux/writer.go @@ -100,7 +100,7 @@ func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error { return nil } -func (w *Writer) Close() { +func (w *Writer) Close() error { meta := FrameMetadata{ SessionID: w.id, SessionStatus: SessionStatusEnd, @@ -110,4 +110,5 @@ func (w *Writer) Close() { common.Must(frame.Reset(meta.AsSupplier())) w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame)) + return nil } diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index ccd988455..13671309e 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -39,7 +39,7 @@ func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) func (*Manager) Start() error { return nil } // Close implements core.Feature -func (*Manager) Close() {} +func (*Manager) Close() error { return nil } // GetDefaultHandler implements core.OutboundHandlerManager. func (m *Manager) GetDefaultHandler() core.OutboundHandler { diff --git a/app/router/router.go b/app/router/router.go index 7d003dc4c..5e0109d5b 100644 --- a/app/router/router.go +++ b/app/router/router.go @@ -114,7 +114,9 @@ func (*Router) Start() error { return nil } -func (*Router) Close() {} +func (*Router) Close() error { + return nil +} func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { diff --git a/clock.go b/clock.go index 5489cae63..54bcb905b 100644 --- a/clock.go +++ b/clock.go @@ -40,15 +40,15 @@ func (c *syncClock) Start() error { return c.Clock.Start() } -func (c *syncClock) Close() { +func (c *syncClock) Close() error { c.RLock() defer c.RUnlock() if c.Clock == nil { - return + return nil } - c.Clock.Close() + return c.Clock.Close() } func (c *syncClock) Set(clock Clock) { diff --git a/commander.go b/commander.go index f65f0072f..63209bfdf 100644 --- a/commander.go +++ b/commander.go @@ -44,15 +44,15 @@ func (c *syncCommander) Start() error { return c.Commander.Start() } -func (c *syncCommander) Close() { +func (c *syncCommander) Close() error { c.RLock() defer c.RUnlock() if c.Commander == nil { - return + return nil } - c.Commander.Close() + return c.Commander.Close() } func (c *syncCommander) Set(commander Commander) { diff --git a/common/interfaces.go b/common/interfaces.go index 6ce590c23..d5cc177e6 100644 --- a/common/interfaces.go +++ b/common/interfaces.go @@ -1,10 +1,23 @@ package common +// Closable is the interface for objects that can release its resources. +type Closable interface { + // Close release all resources used by this object, including goroutines. + Close() error +} + +// Close closes the obj if it is a Closable. +func Close(obj interface{}) error { + if c, ok := obj.(Closable); ok { + return c.Close() + } + return nil +} + // Runnable is the interface for objects that can start to work and stop on demand. type Runnable interface { // Start starts the runnable object. Upon the method returning nil, the object begins to function properly. Start() error - // Close stops the object being working. - Close() + Closable } diff --git a/common/signal/done.go b/common/signal/done.go new file mode 100644 index 000000000..8cffb62ed --- /dev/null +++ b/common/signal/done.go @@ -0,0 +1,48 @@ +package signal + +import ( + "sync" +) + +type Done struct { + access sync.Mutex + c chan struct{} + closed bool +} + +func NewDone() *Done { + return &Done{ + c: make(chan struct{}), + } +} + +func (d *Done) Done() bool { + select { + case <-d.c: + return true + default: + return false + } +} + +func (d *Done) C() chan struct{} { + return d.c +} + +func (d *Done) Wait() { + <-d.c +} + +func (d *Done) Close() error { + d.access.Lock() + defer d.access.Unlock() + + if d.closed { + return nil + } + + d.closed = true + close(d.c) + + return nil +} diff --git a/common/signal/notifier.go b/common/signal/notifier.go index 0d98c2205..a4c4d5b55 100644 --- a/common/signal/notifier.go +++ b/common/signal/notifier.go @@ -1,22 +1,22 @@ package signal type Notifier struct { - c chan bool + c chan struct{} } func NewNotifier() *Notifier { return &Notifier{ - c: make(chan bool, 1), + c: make(chan struct{}, 1), } } func (n *Notifier) Signal() { select { - case n.c <- true: + case n.c <- struct{}{}: default: } } -func (n *Notifier) Wait() <-chan bool { +func (n *Notifier) Wait() <-chan struct{} { return n.c } diff --git a/common/signal/semaphore.go b/common/signal/semaphore.go index 034a4ee77..f9a80db5b 100644 --- a/common/signal/semaphore.go +++ b/common/signal/semaphore.go @@ -1,23 +1,23 @@ package signal type Semaphore struct { - token chan bool + token chan struct{} } func NewSemaphore(n int) *Semaphore { s := &Semaphore{ - token: make(chan bool, n), + token: make(chan struct{}, n), } for i := 0; i < n; i++ { - s.token <- true + s.token <- struct{}{} } return s } -func (s *Semaphore) Wait() <-chan bool { +func (s *Semaphore) Wait() <-chan struct{} { return s.token } func (s *Semaphore) Signal() { - s.token <- true + s.token <- struct{}{} } diff --git a/common/signal/task.go b/common/signal/task.go new file mode 100644 index 000000000..9d8768048 --- /dev/null +++ b/common/signal/task.go @@ -0,0 +1,60 @@ +package signal + +import ( + "sync" + "time" +) + +type PeriodicTask struct { + Interval time.Duration + Execute func() error + + access sync.Mutex + timer *time.Timer + closed bool +} + +func (t *PeriodicTask) checkedExecute() error { + t.access.Lock() + defer t.access.Unlock() + + if t.closed { + return nil + } + + if err := t.Execute(); err != nil { + return err + } + + t.timer = time.AfterFunc(t.Interval, func() { + t.checkedExecute() + }) + + return nil +} + +func (t *PeriodicTask) Start() error { + t.access.Lock() + t.closed = false + t.access.Unlock() + + if err := t.checkedExecute(); err != nil { + t.closed = true + return err + } + + return nil +} + +func (t *PeriodicTask) Close() error { + t.access.Lock() + defer t.access.Unlock() + + t.closed = true + if t.timer != nil { + t.timer.Stop() + t.timer = nil + } + + return nil +} diff --git a/common/signal/timer.go b/common/signal/timer.go index 30989b2f4..6a63af054 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -10,14 +10,14 @@ type ActivityUpdater interface { } type ActivityTimer struct { - updated chan bool + updated chan struct{} timeout chan time.Duration - closing chan bool + closing chan struct{} } func (t *ActivityTimer) Update() { select { - case t.updated <- true: + case t.updated <- struct{}{}: default: } } @@ -72,8 +72,8 @@ func (t *ActivityTimer) run(ctx context.Context, cancel context.CancelFunc) { func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer { timer := &ActivityTimer{ timeout: make(chan time.Duration, 1), - updated: make(chan bool, 1), - closing: make(chan bool), + updated: make(chan struct{}, 1), + closing: make(chan struct{}), } timer.timeout <- timeout go timer.run(ctx, cancel) diff --git a/dns.go b/dns.go index 0c8046e19..5e94d1c2a 100644 --- a/dns.go +++ b/dns.go @@ -1,7 +1,11 @@ package core -import "net" -import "sync" +import ( + "net" + "sync" + + "v2ray.com/core/common" +) // DNSClient is a V2Ray feature for querying DNS information. type DNSClient interface { @@ -36,13 +40,11 @@ func (d *syncDNSClient) Start() error { return d.DNSClient.Start() } -func (d *syncDNSClient) Close() { +func (d *syncDNSClient) Close() error { d.RLock() defer d.RUnlock() - if d.DNSClient != nil { - d.DNSClient.Close() - } + return common.Close(d.DNSClient) } func (d *syncDNSClient) Set(client DNSClient) { diff --git a/network.go b/network.go index 1d2f50910..d526a91f1 100644 --- a/network.go +++ b/network.go @@ -21,6 +21,7 @@ type InboundHandler interface { // OutboundHandler is the interface for handlers that process outbound connections. type OutboundHandler interface { + common.Runnable Tag() string Dispatch(ctx context.Context, outboundRay ray.OutboundRay) } @@ -75,13 +76,11 @@ func (m *syncInboundHandlerManager) Start() error { return m.InboundHandlerManager.Start() } -func (m *syncInboundHandlerManager) Close() { +func (m *syncInboundHandlerManager) Close() error { m.RLock() defer m.RUnlock() - if m.InboundHandlerManager != nil { - m.InboundHandlerManager.Close() - } + return common.Close(m.InboundHandlerManager) } func (m *syncInboundHandlerManager) Set(manager InboundHandlerManager) { @@ -154,13 +153,11 @@ func (m *syncOutboundHandlerManager) Start() error { return m.OutboundHandlerManager.Start() } -func (m *syncOutboundHandlerManager) Close() { +func (m *syncOutboundHandlerManager) Close() error { m.RLock() defer m.RUnlock() - if m.OutboundHandlerManager != nil { - m.OutboundHandlerManager.Close() - } + return common.Close(m.OutboundHandlerManager) } func (m *syncOutboundHandlerManager) Set(manager OutboundHandlerManager) { diff --git a/policy.go b/policy.go index 12cff0f10..a07173206 100644 --- a/policy.go +++ b/policy.go @@ -3,6 +3,8 @@ package core import ( "sync" "time" + + "v2ray.com/core/common" ) // TimeoutPolicy contains limits for connection timeout. @@ -96,13 +98,11 @@ func (m *syncPolicyManager) Start() error { return m.PolicyManager.Start() } -func (m *syncPolicyManager) Close() { +func (m *syncPolicyManager) Close() error { m.RLock() defer m.RUnlock() - if m.PolicyManager != nil { - m.PolicyManager.Close() - } + return common.Close(m.PolicyManager) } func (m *syncPolicyManager) Set(manager PolicyManager) { diff --git a/proxy/vmess/encoding/encoding_test.go b/proxy/vmess/encoding/encoding_test.go index ee6b1a1d6..759705900 100644 --- a/proxy/vmess/encoding/encoding_test.go +++ b/proxy/vmess/encoding/encoding_test.go @@ -48,8 +48,9 @@ func TestRequestSerialization(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) sessionHistory := NewSessionHistory() - userValidator := vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash) + userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator.Add(user) + defer common.Close(userValidator) server := NewServerSession(userValidator, sessionHistory) actualRequest, err := server.DecodeRequestHeader(buffer) diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index 8a81a33a9..8806540fb 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -30,28 +30,34 @@ type sessionId struct { type SessionHistory struct { sync.RWMutex cache map[sessionId]time.Time - token *signal.Semaphore - timer *time.Timer + task *signal.PeriodicTask } func NewSessionHistory() *SessionHistory { h := &SessionHistory{ cache: make(map[sessionId]time.Time, 128), - token: signal.NewSemaphore(1), } + h.task = &signal.PeriodicTask{ + Interval: time.Second * 30, + Execute: func() error { + h.removeExpiredEntries() + return nil + }, + } + common.Must(h.task.Start()) return h } +// Close implements common.Closable. +func (h *SessionHistory) Close() error { + return h.task.Close() +} + func (h *SessionHistory) add(session sessionId) { h.Lock() defer h.Unlock() h.cache[session] = time.Now().Add(time.Minute * 3) - select { - case <-h.token.Wait(): - h.timer = time.AfterFunc(time.Minute*3, h.removeExpiredEntries) - default: - } } func (h *SessionHistory) has(session sessionId) bool { @@ -75,11 +81,6 @@ func (h *SessionHistory) removeExpiredEntries() { delete(h.cache, session) } } - - if h.timer != nil { - h.timer.Stop() - h.timer = nil - } } type ServerSession struct { diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 7490cbc74..26deba018 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -90,7 +90,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) { handler := &Handler{ policyManager: v.PolicyManager(), inboundHandlerManager: v.InboundHandlerManager(), - clients: vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash), + clients: vmess.NewTimedUserValidator(protocol.DefaultIDHash), detours: config.Detour, usersByEmail: newUserByEmail(config.User, config.GetDefaultValue()), sessionHistory: encoding.NewSessionHistory(), @@ -105,6 +105,14 @@ func New(ctx context.Context, config *Config) (*Handler, error) { return handler, nil } +// Close implements common.Closable. +func (h *Handler) Close() error { + common.Close(h.clients) + common.Close(h.sessionHistory) + common.Close(h.usersByEmail) + return nil +} + // Network implements proxy.Inbound.Network(). func (*Handler) Network() net.NetworkList { return net.NetworkList{ diff --git a/proxy/vmess/vmess.go b/proxy/vmess/vmess.go index 9c805fa23..4c1cc5c35 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -8,17 +8,17 @@ package vmess //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg vmess -path Proxy,VMess import ( - "context" "sync" "time" "v2ray.com/core/common" "v2ray.com/core/common/protocol" + "v2ray.com/core/common/signal" ) const ( - updateIntervalSec = 10 - cacheDurationSec = 120 + updateInterval = 10 * time.Second + cacheDurationSec = 120 ) type idEntry struct { @@ -34,6 +34,7 @@ type TimedUserValidator struct { ids []*idEntry hasher protocol.IDHash baseTime protocol.Timestamp + task *signal.PeriodicTask } type indexTimePair struct { @@ -41,16 +42,23 @@ type indexTimePair struct { timeInc uint32 } -func NewTimedUserValidator(ctx context.Context, hasher protocol.IDHash) protocol.UserValidator { - tus := &TimedUserValidator{ +func NewTimedUserValidator(hasher protocol.IDHash) protocol.UserValidator { + tuv := &TimedUserValidator{ validUsers: make([]*protocol.User, 0, 16), userHash: make(map[[16]byte]indexTimePair, 512), ids: make([]*idEntry, 0, 512), hasher: hasher, baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*3), } - go tus.updateUserHash(ctx, updateIntervalSec*time.Second) - return tus + tuv.task = &signal.PeriodicTask{ + Interval: updateInterval, + Execute: func() error { + tuv.updateUserHash() + return nil + }, + } + tuv.task.Start() + return tuv } func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, idx int, entry *idEntry) { @@ -78,24 +86,19 @@ func (v *TimedUserValidator) removeExpiredHashes(expire uint32) { } } -func (v *TimedUserValidator) updateUserHash(ctx context.Context, interval time.Duration) { - for { - select { - case now := <-time.After(interval): - nowSec := protocol.Timestamp(now.Unix() + cacheDurationSec) - v.Lock() - for _, entry := range v.ids { - v.generateNewHashes(nowSec, entry.userIdx, entry) - } +func (v *TimedUserValidator) updateUserHash() { + now := time.Now() + nowSec := protocol.Timestamp(now.Unix() + cacheDurationSec) + v.Lock() + defer v.Unlock() - expire := protocol.Timestamp(now.Unix() - cacheDurationSec*3) - if expire > v.baseTime { - v.removeExpiredHashes(uint32(expire - v.baseTime)) - } - v.Unlock() - case <-ctx.Done(): - return - } + for _, entry := range v.ids { + v.generateNewHashes(nowSec, entry.userIdx, entry) + } + + expire := protocol.Timestamp(now.Unix() - cacheDurationSec*3) + if expire > v.baseTime { + v.removeExpiredHashes(uint32(expire - v.baseTime)) } } @@ -145,3 +148,8 @@ func (v *TimedUserValidator) Get(userHash []byte) (*protocol.User, protocol.Time } return nil, 0, false } + +// Close implements common.Closable. +func (v *TimedUserValidator) Close() error { + return v.task.Close() +} diff --git a/router.go b/router.go index 260806203..d3a8031f0 100644 --- a/router.go +++ b/router.go @@ -4,6 +4,7 @@ import ( "context" "sync" + "v2ray.com/core/common" "v2ray.com/core/common/errors" "v2ray.com/core/common/net" "v2ray.com/core/transport/ray" @@ -45,13 +46,11 @@ func (d *syncDispatcher) Start() error { return d.Dispatcher.Start() } -func (d *syncDispatcher) Close() { +func (d *syncDispatcher) Close() error { d.RLock() defer d.RUnlock() - if d.Dispatcher != nil { - d.Dispatcher.Close() - } + return common.Close(d.Dispatcher) } func (d *syncDispatcher) Set(disp Dispatcher) { @@ -101,13 +100,11 @@ func (r *syncRouter) Start() error { return r.Router.Start() } -func (r *syncRouter) Close() { +func (r *syncRouter) Close() error { r.RLock() defer r.RUnlock() - if r.Router != nil { - r.Router.Close() - } + return common.Close(r.Router) } func (r *syncRouter) Set(router Router) { diff --git a/testing/servers/http/http.go b/testing/servers/http/http.go index 230e973ce..6bada232c 100644 --- a/testing/servers/http/http.go +++ b/testing/servers/http/http.go @@ -36,6 +36,6 @@ func (s *Server) Start() (net.Destination, error) { return net.TCPDestination(net.LocalHostIP, net.Port(s.Port)), nil } -func (s *Server) Close() { - s.server.Close() +func (s *Server) Close() error { + return s.server.Close() } diff --git a/testing/servers/tcp/tcp.go b/testing/servers/tcp/tcp.go index 48c819c00..a54e4b395 100644 --- a/testing/servers/tcp/tcp.go +++ b/testing/servers/tcp/tcp.go @@ -69,6 +69,6 @@ func (server *Server) handleConnection(conn net.Conn) { conn.Close() } -func (server *Server) Close() { - server.listener.Close() +func (server *Server) Close() error { + return server.listener.Close() } diff --git a/testing/servers/udp/udp.go b/testing/servers/udp/udp.go index ff4c789db..22e476890 100644 --- a/testing/servers/udp/udp.go +++ b/testing/servers/udp/udp.go @@ -46,7 +46,7 @@ func (server *Server) handleConnection(conn *net.UDPConn) { } } -func (server *Server) Close() { +func (server *Server) Close() error { server.accepting = false - server.conn.Close() + return server.conn.Close() } diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index c46de60ca..3fdcbf9f4 100644 --- a/transport/internet/kcp/listener.go +++ b/transport/internet/kcp/listener.go @@ -23,7 +23,6 @@ type ConnectionID struct { // Listener defines a server listening for connections type Listener struct { sync.Mutex - ctx context.Context sessions map[ConnectionID]*Connection hub *udp.Hub tlsConfig *tls.Config @@ -31,10 +30,10 @@ type Listener struct { reader PacketReader header internet.PacketHeader security cipher.AEAD - addConn internet.AddConnection + addConn internet.ConnHandler } -func NewListener(ctx context.Context, address net.Address, port net.Port, addConn internet.AddConnection) (*Listener, error) { +func NewListener(ctx context.Context, address net.Address, port net.Port, addConn internet.ConnHandler) (*Listener, error) { networkSettings := internet.TransportSettingsFromContext(ctx) kcpSettings := networkSettings.(*Config) @@ -54,7 +53,6 @@ func NewListener(ctx context.Context, address net.Address, port net.Port, addCon Security: security, }, sessions: make(map[ConnectionID]*Connection), - ctx: ctx, config: kcpSettings, addConn: addConn, } @@ -86,12 +84,6 @@ func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination, originalD l.Lock() defer l.Unlock() - select { - case <-l.ctx.Done(): - return - default: - } - if l.hub == nil { return } @@ -136,23 +128,16 @@ func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination, originalD netConn = tlsConn } - if !l.addConn(context.Background(), netConn) { - return - } + l.addConn(netConn) l.sessions[id] = conn } conn.Input(segments) } func (l *Listener) Remove(id ConnectionID) { - select { - case <-l.ctx.Done(): - return - default: - l.Lock() - delete(l.sessions, id) - l.Unlock() - } + l.Lock() + delete(l.sessions, id) + l.Unlock() } // Close stops listening on the UDP address. Already Accepted connections are not closed. @@ -197,7 +182,7 @@ func (w *Writer) Close() error { return nil } -func ListenKCP(ctx context.Context, address net.Address, port net.Port, addConn internet.AddConnection) (internet.Listener, error) { +func ListenKCP(ctx context.Context, address net.Address, port net.Port, addConn internet.ConnHandler) (internet.Listener, error) { return NewListener(ctx, address, port, addConn) } diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 4047c71aa..05c7e4eb3 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -3,10 +3,10 @@ package tcp import ( "context" gotls "crypto/tls" + "strings" "v2ray.com/core/common" "v2ray.com/core/common/net" - "v2ray.com/core/common/retry" "v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet/tls" ) @@ -17,11 +17,11 @@ type Listener struct { tlsConfig *gotls.Config authConfig internet.ConnectionAuthenticator config *Config - addConn internet.AddConnection + addConn internet.ConnHandler } // ListenTCP creates a new Listener based on configurations. -func ListenTCP(ctx context.Context, address net.Address, port net.Port, addConn internet.AddConnection) (internet.Listener, error) { +func ListenTCP(ctx context.Context, address net.Address, port net.Port, handler internet.ConnHandler) (internet.Listener, error) { listener, err := net.ListenTCP("tcp", &net.TCPAddr{ IP: address.IP(), Port: int(port), @@ -36,7 +36,7 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, addConn l := &Listener{ listener: listener, config: tcpSettings, - addConn: addConn, + addConn: handler, } if config := tls.ConfigFromContext(ctx, tls.WithNextProto("h2")); config != nil { @@ -54,27 +54,17 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, addConn } l.authConfig = auth } - go l.keepAccepting(ctx) + go l.keepAccepting() return l, nil } -func (v *Listener) keepAccepting(ctx context.Context) { +func (v *Listener) keepAccepting() { for { - select { - case <-ctx.Done(): - return - default: - } - var conn net.Conn - err := retry.ExponentialBackoff(5, 200).On(func() error { - rawConn, err := v.listener.Accept() - if err != nil { - return err - } - conn = rawConn - return nil - }) + conn, err := v.listener.Accept() if err != nil { + if strings.Contains(err.Error(), "closed") { + break + } newError("failed to accepted raw connections").Base(err).AtWarning().WriteToLog() continue } @@ -86,7 +76,7 @@ func (v *Listener) keepAccepting(ctx context.Context) { conn = v.authConfig.Server(conn) } - v.addConn(context.Background(), internet.Connection(conn)) + v.addConn(internet.Connection(conn)) } } diff --git a/transport/internet/tcp_hub.go b/transport/internet/tcp_hub.go index 5f1a1d294..081b04aa4 100644 --- a/transport/internet/tcp_hub.go +++ b/transport/internet/tcp_hub.go @@ -2,7 +2,6 @@ package internet import ( "context" - "time" "v2ray.com/core/common/net" ) @@ -19,16 +18,16 @@ func RegisterTransportListener(protocol TransportProtocol, listener ListenFunc) return nil } -type AddConnection func(context.Context, Connection) bool +type ConnHandler func(Connection) -type ListenFunc func(ctx context.Context, address net.Address, port net.Port, addConn AddConnection) (Listener, error) +type ListenFunc func(ctx context.Context, address net.Address, port net.Port, handler ConnHandler) (Listener, error) type Listener interface { Close() error Addr() net.Addr } -func ListenTCP(ctx context.Context, address net.Address, port net.Port, conns chan<- Connection) (Listener, error) { +func ListenTCP(ctx context.Context, address net.Address, port net.Port, handler ConnHandler) (Listener, error) { settings := StreamSettingsFromContext(ctx) protocol := settings.GetEffectiveProtocol() transportSettings, err := settings.GetEffectiveTransportSettings() @@ -47,26 +46,7 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, conns ch if listenFunc == nil { return nil, newError(protocol, " listener not registered.").AtError() } - listener, err := listenFunc(ctx, address, port, func(ctx context.Context, conn Connection) bool { - select { - case <-ctx.Done(): - conn.Close() - return false - case conns <- conn: - return true - default: - select { - case <-ctx.Done(): - conn.Close() - return false - case conns <- conn: - return true - case <-time.After(time.Second * 5): - conn.Close() - return false - } - } - }) + listener, err := listenFunc(ctx, address, port, handler) if err != nil { return nil, newError("failed to listen on address: ", address, ":", port).Base(err) } diff --git a/transport/internet/udp/dispatcher_test.go b/transport/internet/udp/dispatcher_test.go index 989cb6510..e6c1958ab 100644 --- a/transport/internet/udp/dispatcher_test.go +++ b/transport/internet/udp/dispatcher_test.go @@ -25,7 +25,9 @@ func (d *TestDispatcher) Start() error { return nil } -func (d *TestDispatcher) Close() {} +func (d *TestDispatcher) Close() error { + return nil +} func TestSameDestinationDispatching(t *testing.T) { assert := With(t) diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index d6b151784..cc96ca682 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -60,10 +60,11 @@ func (q *PayloadQueue) Dequeue(queue <-chan Payload) { } } -func (q *PayloadQueue) Close() { +func (q *PayloadQueue) Close() error { for _, queue := range q.queue { close(queue) } + return nil } type ListenOption struct { @@ -116,9 +117,10 @@ func ListenUDP(address net.Address, port net.Port, option ListenOption) (*Hub, e return hub, nil } -func (h *Hub) Close() { +func (h *Hub) Close() error { h.cancel() h.conn.Close() + return nil } func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) { diff --git a/transport/internet/websocket/hub.go b/transport/internet/websocket/hub.go index a5ec0eb0f..36275fe2f 100644 --- a/transport/internet/websocket/hub.go +++ b/transport/internet/websocket/hub.go @@ -44,24 +44,22 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req remoteAddr.(*net.TCPAddr).IP = forwardedAddrs[0].IP() } - h.ln.addConn(h.ln.ctx, newConnection(conn, remoteAddr)) + h.ln.addConn(newConnection(conn, remoteAddr)) } type Listener struct { sync.Mutex - ctx context.Context listener net.Listener tlsConfig *tls.Config config *Config - addConn internet.AddConnection + addConn internet.ConnHandler } -func ListenWS(ctx context.Context, address net.Address, port net.Port, addConn internet.AddConnection) (internet.Listener, error) { +func ListenWS(ctx context.Context, address net.Address, port net.Port, addConn internet.ConnHandler) (internet.Listener, error) { networkSettings := internet.TransportSettingsFromContext(ctx) wsSettings := networkSettings.(*Config) l := &Listener{ - ctx: ctx, config: wsSettings, addConn: addConn, } diff --git a/transport/ray/direct.go b/transport/ray/direct.go index efcb71cd4..53af0f4d5 100644 --- a/transport/ray/direct.go +++ b/transport/ray/direct.go @@ -209,12 +209,13 @@ func (s *Stream) WriteMultiBuffer(data buf.MultiBuffer) error { } // Close closes the stream for writing. Read() still works until EOF. -func (s *Stream) Close() { +func (s *Stream) Close() error { s.access.Lock() s.close = true s.readSignal.Signal() s.writeSignal.Signal() s.access.Unlock() + return nil } // CloseError closes the Stream with error. Read() will return an error afterwards. diff --git a/transport/ray/ray.go b/transport/ray/ray.go index 46d41f7bc..557bc64eb 100644 --- a/transport/ray/ray.go +++ b/transport/ray/ray.go @@ -1,6 +1,9 @@ package ray -import "v2ray.com/core/common/buf" +import ( + "v2ray.com/core/common" + "v2ray.com/core/common/buf" +) // OutboundRay is a transport interface for outbound connections. type OutboundRay interface { @@ -34,7 +37,7 @@ type Ray interface { } type RayStream interface { - Close() + common.Closable CloseError() } diff --git a/v2ray.go b/v2ray.go index 7bca7babc..af710fd36 100644 --- a/v2ray.go +++ b/v2ray.go @@ -101,7 +101,7 @@ func (s *Instance) ID() uuid.UUID { } // Close shutdown the V2Ray instance. -func (s *Instance) Close() { +func (s *Instance) Close() error { s.access.Lock() defer s.access.Unlock() @@ -109,6 +109,8 @@ func (s *Instance) Close() { for _, f := range s.features { f.Close() } + + return nil } // Start starts the V2Ray instance, including all registered features. When Start returns error, the state of the instance is unknown. From 9a46cf37fb68dc8abb7c70700111cbe24228f85b Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 15:45:50 +0100 Subject: [PATCH 37/63] fix default outbound handler --- app/proxyman/outbound/handler.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index c792994eb..9ef25c160 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -127,3 +127,11 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn func (h *Handler) GetOutbound() proxy.Outbound { return h.proxy } + +func (h *Handler) Start() error { + return nil +} + +func (h *Handler) Close() error { + return nil +} From 5755d4153a521d20b20cf5dfba56c1717c039a82 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 16:13:27 +0100 Subject: [PATCH 38/63] fix encoding test --- proxy/vmess/encoding/encoding_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/proxy/vmess/encoding/encoding_test.go b/proxy/vmess/encoding/encoding_test.go index 759705900..794173921 100644 --- a/proxy/vmess/encoding/encoding_test.go +++ b/proxy/vmess/encoding/encoding_test.go @@ -1,7 +1,6 @@ package encoding_test import ( - "context" "testing" "v2ray.com/core/common" @@ -45,8 +44,8 @@ func TestRequestSerialization(t *testing.T) { buffer2 := buf.New() buffer2.Append(buffer.Bytes()) - ctx, cancel := context.WithCancel(context.Background()) sessionHistory := NewSessionHistory() + defer common.Close(sessionHistory) userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator.Add(user) @@ -66,6 +65,4 @@ func TestRequestSerialization(t *testing.T) { _, err = server.DecodeRequestHeader(buffer2) // anti replay attack assert(err, IsNotNil) - - cancel() } From c368412728c90327ea26a68d27ff38e5ff45e09a Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 16:19:43 +0100 Subject: [PATCH 39/63] test case for periodic task --- common/signal/task_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 common/signal/task_test.go diff --git a/common/signal/task_test.go b/common/signal/task_test.go new file mode 100644 index 000000000..75be9742b --- /dev/null +++ b/common/signal/task_test.go @@ -0,0 +1,29 @@ +package signal_test + +import ( + "testing" + "time" + + "v2ray.com/core/common" + . "v2ray.com/core/common/signal" + . "v2ray.com/ext/assert" +) + +func TestPeriodicTaskStop(t *testing.T) { + assert := With(t) + + value := 0 + task := &PeriodicTask{ + Interval: time.Second * 2, + Execute: func() error { + value++ + return nil + }, + } + common.Must(task.Start()) + time.Sleep(time.Second * 5) + common.Must(task.Close()) + assert(value, Equals, 3) + time.Sleep(time.Second * 4) + assert(value, Equals, 3) +} From 9fdb783729a360cd582da0baa49cbe7acbfbd865 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 16:22:44 +0100 Subject: [PATCH 40/63] fix kcp test --- transport/internet/kcp/kcp_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/transport/internet/kcp/kcp_test.go b/transport/internet/kcp/kcp_test.go index d7625b3b4..7487cf1aa 100644 --- a/transport/internet/kcp/kcp_test.go +++ b/transport/internet/kcp/kcp_test.go @@ -17,7 +17,7 @@ import ( func TestDialAndListen(t *testing.T) { assert := With(t) - listerner, err := NewListener(internet.ContextWithTransportSettings(context.Background(), &Config{}), net.LocalHostIP, net.Port(0), func(ctx context.Context, conn internet.Connection) bool { + listerner, err := NewListener(internet.ContextWithTransportSettings(context.Background(), &Config{}), net.LocalHostIP, net.Port(0), func(conn internet.Connection) { go func(c internet.Connection) { payload := make([]byte, 4096) for { @@ -32,7 +32,6 @@ func TestDialAndListen(t *testing.T) { } c.Close() }(conn) - return true }) assert(err, IsNil) port := net.Port(listerner.Addr().(*net.UDPAddr).Port) From acc6ea3e6beee3c9c963f99459c0f2437c7cfbc5 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 16:39:18 +0100 Subject: [PATCH 41/63] fix websocket test --- transport/internet/websocket/ws_test.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/transport/internet/websocket/ws_test.go b/transport/internet/websocket/ws_test.go index 4f3c906cf..1cb985b6b 100644 --- a/transport/internet/websocket/ws_test.go +++ b/transport/internet/websocket/ws_test.go @@ -18,7 +18,7 @@ func Test_listenWSAndDial(t *testing.T) { assert := With(t) listen, err := ListenWS(internet.ContextWithTransportSettings(context.Background(), &Config{ Path: "ws", - }), net.DomainAddress("localhost"), 13146, func(ctx context.Context, conn internet.Connection) bool { + }), net.DomainAddress("localhost"), 13146, func(conn internet.Connection) { go func(c internet.Connection) { defer c.Close() @@ -33,7 +33,6 @@ func Test_listenWSAndDial(t *testing.T) { _, err = c.Write([]byte("Response")) assert(err, IsNil) }(conn) - return true }) assert(err, IsNil) @@ -67,7 +66,7 @@ func TestDialWithRemoteAddr(t *testing.T) { assert := With(t) listen, err := ListenWS(internet.ContextWithTransportSettings(context.Background(), &Config{ Path: "ws", - }), net.DomainAddress("localhost"), 13148, func(ctx context.Context, conn internet.Connection) bool { + }), net.DomainAddress("localhost"), 13148, func(conn internet.Connection) { go func(c internet.Connection) { defer c.Close() @@ -84,7 +83,6 @@ func TestDialWithRemoteAddr(t *testing.T) { _, err = c.Write([]byte("Response")) assert(err, IsNil) }(conn) - return true }) assert(err, IsNil) @@ -115,11 +113,10 @@ func Test_listenWSAndDial_TLS(t *testing.T) { AllowInsecure: true, Certificate: []*v2tls.Certificate{tlsgen.GenerateCertificateForTest()}, }) - listen, err := ListenWS(ctx, net.DomainAddress("localhost"), 13143, func(ctx context.Context, conn internet.Connection) bool { + listen, err := ListenWS(ctx, net.DomainAddress("localhost"), 13143, func(conn internet.Connection) { go func() { _ = conn.Close() }() - return true }) assert(err, IsNil) defer listen.Close() From dffaef60b458ba1f99c7ef6814213cad115b4785 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 17:00:22 +0100 Subject: [PATCH 42/63] use periodic task in dns.Server --- app/dns/server.go | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/app/dns/server.go b/app/dns/server.go index ba273b53d..2972ff5cf 100644 --- a/app/dns/server.go +++ b/app/dns/server.go @@ -11,6 +11,7 @@ import ( "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/net" + "v2ray.com/core/common/signal" ) const ( @@ -37,6 +38,7 @@ type Server struct { hosts map[string]net.IP records map[string]*DomainRecord servers []NameServer + task *signal.PeriodicTask } func New(ctx context.Context, config *Config) (*Server, error) { @@ -45,6 +47,13 @@ func New(ctx context.Context, config *Config) (*Server, error) { servers: make([]NameServer, len(config.NameServers)), hosts: config.GetInternalHosts(), } + server.task = &signal.PeriodicTask{ + Interval: time.Minute * 10, + Execute: func() error { + server.cleanup() + return nil + }, + } v := core.FromContext(ctx) if v == nil { return nil, newError("V is not in context.") @@ -75,16 +84,14 @@ func New(ctx context.Context, config *Config) (*Server, error) { return server, nil } -func (*Server) Interface() interface{} { - return (*Server)(nil) -} - +// Start implements common.Runnable. func (s *Server) Start() error { - return nil + return s.task.Start() } -func (*Server) Close() error { - return nil +// Close implements common.Runnable. +func (s *Server) Close() error { + return s.task.Close() } func (s *Server) GetCached(domain string) []net.IP { @@ -98,18 +105,12 @@ func (s *Server) GetCached(domain string) []net.IP { return nil } -func (s *Server) tryCleanup() { +func (s *Server) cleanup() { s.Lock() defer s.Unlock() - if len(s.records) > 256 { - domains := make([]string, 0, 256) - for d, r := range s.records { - if r.Expired() { - domains = append(domains, d) - } - } - for _, d := range domains { + for d, r := range s.records { + if r.Expired() { delete(s.records, d) } } @@ -126,8 +127,6 @@ func (s *Server) LookupIP(domain string) ([]net.IP, error) { return ips, nil } - s.tryCleanup() - for _, server := range s.servers { response := server.QueryA(domain) select { From 32f1ba7c7ed9d8634f2240c0b60c69b8bc7070d7 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 17:24:14 +0100 Subject: [PATCH 43/63] properly start and close outbound handlers --- app/proxyman/outbound/outbound.go | 42 +++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 13671309e..52848f324 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -36,10 +36,44 @@ func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) } // Start implements core.Feature -func (*Manager) Start() error { return nil } +func (m *Manager) Start() error { + m.access.Lock() + defer m.access.Unlock() + + m.running = true + + for _, h := range m.taggedHandler { + if err := h.Start(); err != nil { + return err + } + } + + for _, h := range m.untaggedHandlers { + if err := h.Start(); err != nil { + return err + } + } + + return nil +} // Close implements core.Feature -func (*Manager) Close() error { return nil } +func (m *Manager) Close() error { + m.access.Lock() + defer m.access.Unlock() + + m.running = false + + for _, h := range m.taggedHandler { + h.Close() + } + + for _, h := range m.untaggedHandlers { + h.Close() + } + + return nil +} // GetDefaultHandler implements core.OutboundHandlerManager. func (m *Manager) GetDefaultHandler() core.OutboundHandler { @@ -78,6 +112,10 @@ func (m *Manager) AddHandler(ctx context.Context, handler core.OutboundHandler) m.untaggedHandlers = append(m.untaggedHandlers, handler) } + if m.running { + return handler.Start() + } + return nil } From b5facc0ca5d5af7f3a4bef968f95fa9a668fd0a3 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 17:46:50 +0100 Subject: [PATCH 44/63] remove context from mux.Client --- app/proxyman/mux/mux.go | 46 ++++++++++++++------------------ app/proxyman/outbound/handler.go | 5 ++-- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index e2ae68fbc..b921dbd69 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -14,6 +14,7 @@ import ( "v2ray.com/core/common/errors" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" + "v2ray.com/core/common/signal" "v2ray.com/core/proxy" "v2ray.com/core/transport/ray" ) @@ -74,8 +75,7 @@ func (m *ClientManager) onClientFinish() { type Client struct { sessionManager *SessionManager inboundRay ray.InboundRay - ctx context.Context - cancel context.CancelFunc + done *signal.Done manager *ClientManager concurrency uint32 } @@ -85,26 +85,26 @@ var muxCoolPort = net.Port(9527) // NewClient creates a new mux.Client. func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client, error) { - ctx, cancel := context.WithCancel(context.Background()) - ctx = proxy.ContextWithTarget(ctx, net.TCPDestination(muxCoolAddress, muxCoolPort)) + ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(muxCoolAddress, muxCoolPort)) + ctx, cancel := context.WithCancel(ctx) pipe := ray.NewRay(ctx) - go func() { - if err := p.Process(ctx, pipe, dialer); err != nil { - cancel() - - errors.New("failed to handler mux client connection").Base(err).WriteToLog() - } - }() - c := &Client{ sessionManager: NewSessionManager(), inboundRay: pipe, - ctx: ctx, - cancel: cancel, + done: signal.NewDone(), manager: m, concurrency: m.config.Concurrency, } + + go func() { + if err := p.Process(ctx, pipe, dialer); err != nil { + errors.New("failed to handler mux client connection").Base(err).WriteToLog() + } + c.done.Close() + cancel() + }() + go c.fetchOutput() go c.monitor() return c, nil @@ -112,12 +112,7 @@ func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client // Closed returns true if this Client is closed. func (m *Client) Closed() bool { - select { - case <-m.ctx.Done(): - return true - default: - return false - } + return m.done.Done() } func (m *Client) monitor() { @@ -128,7 +123,7 @@ func (m *Client) monitor() { for { select { - case <-m.ctx.Done(): + case <-m.done.C(): m.sessionManager.Close() m.inboundRay.InboundInput().Close() m.inboundRay.InboundOutput().CloseError() @@ -136,7 +131,8 @@ func (m *Client) monitor() { case <-timer.C: size := m.sessionManager.Size() if size == 0 && m.sessionManager.CloseIfNoSession() { - m.cancel() + m.done.Close() + return } } } @@ -170,10 +166,8 @@ func (m *Client) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) bool return false } - select { - case <-m.ctx.Done(): + if m.done.Done() { return false - default: } s := sm.Allocate() @@ -226,7 +220,7 @@ func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader } func (m *Client) fetchOutput() { - defer m.cancel() + defer m.done.Close() reader := buf.NewBufferedReader(m.inboundRay.InboundOutput()) diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index 9ef25c160..98a1d979b 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -93,8 +93,6 @@ func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) { } } -var zeroAddr net.Addr = &net.TCPAddr{IP: []byte{0, 0, 0, 0}, Port: 0} - // Dial implements proxy.Dialer.Dial(). func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) { if h.senderSettings != nil { @@ -124,14 +122,17 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn return internet.Dial(ctx, dest) } +// GetOutbound implements proxy.GetOutbound. func (h *Handler) GetOutbound() proxy.Outbound { return h.proxy } +// Start implements common.Runnable. func (h *Handler) Start() error { return nil } +// Close implements common.Runnable. func (h *Handler) Close() error { return nil } From 0e8d9ae2bf9e1117482949afd71bf79447f403f2 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 22:04:16 +0100 Subject: [PATCH 45/63] Update version --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index 972bebbf8..529937890 100644 --- a/core.go +++ b/core.go @@ -18,7 +18,7 @@ import ( ) var ( - version = "3.7" + version = "3.8" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." From 278fd7261e36fecb5d8ff0ed5991b85f04ada6c5 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 22:09:41 +0100 Subject: [PATCH 46/63] remove context in udp hub --- transport/internet/udp/hub.go | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index cc96ca682..5fb267591 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -1,8 +1,6 @@ package udp import ( - "context" - "v2ray.com/core/common/buf" "v2ray.com/core/common/dice" "v2ray.com/core/common/net" @@ -75,7 +73,6 @@ type ListenOption struct { type Hub struct { conn *net.UDPConn - cancel context.CancelFunc queue *PayloadQueue option ListenOption } @@ -106,19 +103,16 @@ func ListenUDP(address net.Address, port net.Port, option ListenOption) (*Hub, e return nil, newError("failed to control socket").Base(err) } } - ctx, cancel := context.WithCancel(context.Background()) hub := &Hub{ conn: udpConn, queue: NewPayloadQueue(option), option: option, - cancel: cancel, } - go hub.start(ctx) + go hub.start() return hub, nil } func (h *Hub) Close() error { - h.cancel() h.conn.Close() return nil } @@ -130,16 +124,10 @@ func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) { }) } -func (h *Hub) start(ctx context.Context) { +func (h *Hub) start() { oobBytes := make([]byte, 256) -L: - for { - select { - case <-ctx.Done(): - break L - default: - } + for { buffer := buf.New() var noob int var addr *net.UDPAddr @@ -153,7 +141,7 @@ L: if err != nil { newError("failed to read UDP msg").Base(err).WriteToLog() buffer.Release() - continue + break } payload := Payload{ From 862f9a152e1dbf32b08fc17eecfcb2c69c20a8ca Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 22:09:55 +0100 Subject: [PATCH 47/63] comments and refactoring --- app/proxyman/inbound/inbound.go | 3 +++ app/proxyman/inbound/worker.go | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index cfd4cd5c1..e7a190bb4 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -19,6 +19,7 @@ type Manager struct { running bool } +// New returns a new Manager for inbound handlers. func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) { m := &Manager{ taggedHandlers: make(map[string]core.InboundHandler), @@ -33,6 +34,7 @@ func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) return m, nil } +// AddHandler implements core.InboundHandlerManager. func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) error { m.access.Lock() defer m.access.Unlock() @@ -51,6 +53,7 @@ func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) e return nil } +// GetHandler returns core.InboundHandlerManager. func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) { m.access.RLock() defer m.access.RUnlock() diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 95cd882c1..804092fec 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -152,7 +152,7 @@ func (*udpConn) SetWriteDeadline(time.Time) error { return nil } -type connId struct { +type connID struct { src net.Destination dest net.Destination } @@ -169,10 +169,10 @@ type udpWorker struct { dispatcher core.Dispatcher done *signal.Done - activeConn map[connId]*udpConn + activeConn map[connID]*udpConn } -func (w *udpWorker) getConnection(id connId) (*udpConn, bool) { +func (w *udpWorker) getConnection(id connID) (*udpConn, bool) { w.Lock() defer w.Unlock() @@ -202,7 +202,7 @@ func (w *udpWorker) getConnection(id connId) (*udpConn, bool) { } func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) { - id := connId{ + id := connID{ src: source, dest: originalDest, } @@ -235,14 +235,14 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest } } -func (w *udpWorker) removeConn(id connId) { +func (w *udpWorker) removeConn(id connID) { w.Lock() delete(w.activeConn, id) w.Unlock() } func (w *udpWorker) Start() error { - w.activeConn = make(map[connId]*udpConn, 16) + w.activeConn = make(map[connID]*udpConn, 16) w.done = signal.NewDone() h, err := udp.ListenUDP(w.address, w.port, udp.ListenOption{ Callback: w.callback, From 90925bdf7a49bfcddf3fd6e8ef75276cc23d3cac Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 22:10:02 +0100 Subject: [PATCH 48/63] use go 1.9.4 --- .travis.yml | 2 +- release/release-ci.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index df4b91faa..e5f3318f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: required language: go go: -- 1.9.2 +- 1.9.4 go_import_path: v2ray.com/core git: depth: 5 diff --git a/release/release-ci.sh b/release/release-ci.sh index d84876675..51b752211 100755 --- a/release/release-ci.sh +++ b/release/release-ci.sh @@ -25,7 +25,7 @@ echo ${SIGN_KEY_PASS} | gpg --passphrase-fd 0 --batch --import /v2ray/build/sign curl -L -o /v2ray/build/releases https://api.github.com/repos/v2ray/v2ray-core/releases GO_INSTALL=golang.tar.gz -curl -L -o ${GO_INSTALL} https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz +curl -L -o ${GO_INSTALL} https://storage.googleapis.com/golang/go1.9.4.linux-amd64.tar.gz tar -C /usr/local -xzf ${GO_INSTALL} export PATH=$PATH:/usr/local/go/bin From f8ce1945e16678dab1d63a904c934388f8f967e2 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 22:52:54 +0100 Subject: [PATCH 49/63] remove unused code --- common/event/event.go | 46 ------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 common/event/event.go diff --git a/common/event/event.go b/common/event/event.go deleted file mode 100644 index 36a58db50..000000000 --- a/common/event/event.go +++ /dev/null @@ -1,46 +0,0 @@ -package event - -import "sync" - -type Event uint16 - -type Handler func(data interface{}) error - -type Registry interface { - On(Event, Handler) -} - -type Listener struct { - sync.RWMutex - events map[Event][]Handler -} - -func (l *Listener) On(e Event, h Handler) { - l.Lock() - defer l.Unlock() - - if l.events == nil { - l.events = make(map[Event][]Handler) - } - - handlers := l.events[e] - handlers = append(handlers, h) - l.events[e] = handlers -} - -func (l *Listener) Fire(e Event, data interface{}) error { - l.RLock() - defer l.RUnlock() - - if l.events == nil { - return nil - } - - for _, h := range l.events[e] { - if err := h(data); err != nil { - return err - } - } - - return nil -} From 495ae1c5e3a831f5336b8b224e7f6554920b5b24 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 22:53:01 +0100 Subject: [PATCH 50/63] implement remove user operation --- app/proxyman/command/command.go | 34 ++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go index 4d93adcad..8646918c7 100644 --- a/app/proxyman/command/command.go +++ b/app/proxyman/command/command.go @@ -9,20 +9,32 @@ import ( "v2ray.com/core/proxy" ) +// InboundOperation is the interface for operations that applies to inbound handlers. type InboundOperation interface { + // ApplyInbound appliess this operation to the given inbound handler. ApplyInbound(context.Context, core.InboundHandler) error } +// OutboundOperation is the interface for operations that applies to outbound handlers. type OutboundOperation interface { + // ApplyOutbound applies this operation to the given outbound handler. ApplyOutbound(context.Context, core.OutboundHandler) error } -func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler core.InboundHandler) error { - getInbound, ok := handler.(proxy.GetInbound) +func getInbound(handler core.InboundHandler) (proxy.Inbound, error) { + gi, ok := handler.(proxy.GetInbound) if !ok { - return newError("can't get inbound proxy from handler") + return nil, newError("can't get inbound proxy from handler.") + } + return gi.GetInbound(), nil +} + +// ApplyInbound implements InboundOperation. +func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler core.InboundHandler) error { + p, err := getInbound(handler) + if err != nil { + return err } - p := getInbound.GetInbound() um, ok := p.(proxy.UserManager) if !ok { return newError("proxy is not an UserManager") @@ -30,17 +42,17 @@ func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler core.Inbou return um.AddUser(ctx, op.User) } -func (op *AddUserOperation) ApplyOutbound(ctx context.Context, handler core.OutboundHandler) error { - getOutbound, ok := handler.(proxy.GetOutbound) - if !ok { - return newError("can't get outbound proxy from handler") +// ApplyInbound implements InboundOperation. +func (op *RemoveUserOperation) ApplyInbound(ctx context.Context, handler core.InboundHandler) error { + p, err := getInbound(handler) + if err != nil { + return err } - p := getOutbound.GetOutbound() um, ok := p.(proxy.UserManager) if !ok { - return newError("proxy in not an UserManager") + return newError("proxy is not an UserManager") } - return um.AddUser(ctx, op.User) + return um.RemoveUser(ctx, op.Email) } type handlerServer struct { From d9040b571ddb4469a8f70c9f6acb88032fc83815 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 23:24:35 +0100 Subject: [PATCH 51/63] decouple commander interface from grpc --- app/commander/commander.go | 36 +++++++++++++++++---------------- app/commander/config.pb.go | 29 ++++++++++++++++++++------ app/commander/config.proto | 6 ++++++ app/commander/service.go | 9 +++++++++ app/proxyman/command/command.go | 23 +++++++++------------ commander.go | 19 ----------------- 6 files changed, 66 insertions(+), 56 deletions(-) create mode 100644 app/commander/service.go diff --git a/app/commander/commander.go b/app/commander/commander.go index e63935ff3..8b5deeee6 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -14,10 +14,10 @@ import ( type Commander struct { sync.Mutex - server *grpc.Server - config Config - ohm core.OutboundHandlerManager - callbacks []core.ServiceRegistryCallback + server *grpc.Server + config Config + v *core.Instance + ohm core.OutboundHandlerManager } func NewCommander(ctx context.Context, config *Config) (*Commander, error) { @@ -28,6 +28,7 @@ func NewCommander(ctx context.Context, config *Config) (*Commander, error) { c := &Commander{ config: *config, ohm: v.OutboundHandlerManager(), + v: v, } if err := v.RegisterFeature((*core.Commander)(nil), c); err != nil { return nil, err @@ -35,22 +36,23 @@ func NewCommander(ctx context.Context, config *Config) (*Commander, error) { return c, nil } -func (c *Commander) RegisterService(callback core.ServiceRegistryCallback) { - c.Lock() - defer c.Unlock() - - if callback == nil { - return - } - - c.callbacks = append(c.callbacks, callback) -} - func (c *Commander) Start() error { c.Lock() c.server = grpc.NewServer() - for _, callback := range c.callbacks { - callback(c.server) + for _, rawConfig := range c.config.Service { + config, err := rawConfig.GetInstance() + if err != nil { + return err + } + rawService, err := c.v.CreateObject(config) + if err != nil { + return err + } + service, ok := rawService.(Service) + if !ok { + return newError("not a Service.") + } + service.Register(c.server) } c.Unlock() diff --git a/app/commander/config.pb.go b/app/commander/config.pb.go index dbb64bdf8..0ba805e57 100644 --- a/app/commander/config.pb.go +++ b/app/commander/config.pb.go @@ -3,6 +3,7 @@ package commander import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" +import v2ray_core_common_serial "v2ray.com/core/common/serial" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -15,8 +16,12 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +// Config is the settings for Commander. type Config struct { + // Tag of the outbound handler that handles grpc connections. Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` + // Services that supported by this server. All services must implement Service interface. + Service []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,rep,name=service" json:"service,omitempty"` } func (m *Config) Reset() { *m = Config{} } @@ -31,6 +36,13 @@ func (m *Config) GetTag() string { return "" } +func (m *Config) GetService() []*v2ray_core_common_serial.TypedMessage { + if m != nil { + return m.Service + } + return nil +} + func init() { proto.RegisterType((*Config)(nil), "v2ray.core.app.commander.Config") } @@ -38,14 +50,19 @@ func init() { func init() { proto.RegisterFile("v2ray.com/core/app/commander/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 143 bytes of a gzipped FileDescriptorProto + // 212 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2c, 0x33, 0x2a, 0x4a, 0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0x4f, 0xce, 0xcf, 0xcd, 0x4d, 0xcc, 0x4b, 0x49, 0x2d, 0xd2, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x80, 0x29, 0x2d, 0x4a, 0xd5, 0x4b, 0x2c, 0x28, 0xd0, - 0x83, 0x2b, 0x53, 0x92, 0xe2, 0x62, 0x73, 0x06, 0xab, 0x14, 0x12, 0xe0, 0x62, 0x2e, 0x49, 0x4c, - 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x9d, 0xdc, 0xb8, 0x64, 0x92, 0xf3, 0x73, - 0xf5, 0x70, 0xe9, 0x0d, 0x60, 0x8c, 0xe2, 0x84, 0x73, 0x56, 0x31, 0x49, 0x84, 0x19, 0x05, 0x25, - 0x56, 0xea, 0x39, 0x83, 0xd4, 0x39, 0x16, 0x14, 0xe8, 0x39, 0xc3, 0xa4, 0x92, 0xd8, 0xc0, 0x8e, - 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x36, 0x74, 0x98, 0x19, 0xb1, 0x00, 0x00, 0x00, + 0x83, 0x2b, 0x93, 0x32, 0x40, 0x33, 0x04, 0x24, 0x93, 0x9f, 0xa7, 0x5f, 0x9c, 0x5a, 0x94, 0x99, + 0x98, 0xa3, 0x5f, 0x52, 0x59, 0x90, 0x9a, 0x12, 0x9f, 0x9b, 0x5a, 0x5c, 0x9c, 0x98, 0x9e, 0x0a, + 0x31, 0x4b, 0x29, 0x86, 0x8b, 0xcd, 0x19, 0x6c, 0xb6, 0x90, 0x00, 0x17, 0x73, 0x49, 0x62, 0xba, + 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x88, 0x29, 0xe4, 0xc0, 0xc5, 0x5e, 0x9c, 0x5a, 0x54, + 0x96, 0x99, 0x9c, 0x2a, 0xc1, 0xa4, 0xc0, 0xac, 0xc1, 0x6d, 0xa4, 0xa6, 0x87, 0x64, 0x33, 0xc4, + 0x6c, 0x3d, 0x88, 0xd9, 0x7a, 0x21, 0x20, 0xb3, 0x7d, 0x21, 0x46, 0x07, 0xc1, 0xb4, 0x39, 0xb9, + 0x71, 0xc9, 0x24, 0xe7, 0xe7, 0xea, 0xe1, 0x72, 0x6f, 0x00, 0x63, 0x14, 0x27, 0x9c, 0xb3, 0x8a, + 0x49, 0x22, 0xcc, 0x28, 0x28, 0xb1, 0x52, 0xcf, 0x19, 0xa4, 0xce, 0xb1, 0xa0, 0x40, 0xcf, 0x19, + 0x26, 0x95, 0xc4, 0x06, 0x76, 0xac, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x29, 0x02, 0xa6, 0x19, + 0x25, 0x01, 0x00, 0x00, } diff --git a/app/commander/config.proto b/app/commander/config.proto index 56a7b7745..e6aaa7ec6 100644 --- a/app/commander/config.proto +++ b/app/commander/config.proto @@ -6,6 +6,12 @@ option go_package = "commander"; option java_package = "com.v2ray.core.app.commander"; option java_multiple_files = true; +import "v2ray.com/core/common/serial/typed_message.proto"; + +// Config is the settings for Commander. message Config { + // Tag of the outbound handler that handles grpc connections. string tag = 1; + // Services that supported by this server. All services must implement Service interface. + repeated v2ray.core.common.serial.TypedMessage service = 2; } diff --git a/app/commander/service.go b/app/commander/service.go new file mode 100644 index 000000000..fb9340db4 --- /dev/null +++ b/app/commander/service.go @@ -0,0 +1,9 @@ +package commander + +import ( + "google.golang.org/grpc" +) + +type Service interface { + Register(*grpc.Server) +} diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go index 8646918c7..c0357579d 100644 --- a/app/proxyman/command/command.go +++ b/app/proxyman/command/command.go @@ -125,14 +125,16 @@ func (s *handlerServer) AlterOutbound(ctx context.Context, request *AlterOutboun return &AlterOutboundResponse{}, operation.ApplyOutbound(ctx, handler) } -type feature struct{} - -func (*feature) Start() error { - return nil +type service struct { + v *core.Instance } -func (*feature) Close() error { - return nil +func (s *service) Register(server *grpc.Server) { + RegisterHandlerServiceServer(server, &handlerServer{ + s: s.v, + ihm: s.v.InboundHandlerManager(), + ohm: s.v.OutboundHandlerManager(), + }) } func init() { @@ -141,13 +143,6 @@ func init() { if s == nil { return nil, newError("V is not in context.") } - s.Commander().RegisterService(func(server *grpc.Server) { - RegisterHandlerServiceServer(server, &handlerServer{ - s: s, - ihm: s.InboundHandlerManager(), - ohm: s.OutboundHandlerManager(), - }) - }) - return &feature{}, nil + return &service{v: s}, nil })) } diff --git a/commander.go b/commander.go index 63209bfdf..eb2d3750d 100644 --- a/commander.go +++ b/commander.go @@ -2,19 +2,11 @@ package core import ( "sync" - - "google.golang.org/grpc" ) -// ServiceRegistryCallback is a callback function for registering services. -type ServiceRegistryCallback func(s *grpc.Server) - // Commander is a feature that accepts commands from external source. type Commander interface { Feature - - // RegisterService registers a service into this Commander. - RegisterService(ServiceRegistryCallback) } type syncCommander struct { @@ -22,17 +14,6 @@ type syncCommander struct { Commander } -func (c *syncCommander) RegisterService(callback ServiceRegistryCallback) { - c.RLock() - defer c.RUnlock() - - if c.Commander == nil { - return - } - - c.Commander.RegisterService(callback) -} - func (c *syncCommander) Start() error { c.RLock() defer c.RUnlock() From c05dade00a7cd106c939d8739b980fa88c531fe8 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 23:33:40 +0100 Subject: [PATCH 52/63] fix commander test --- testing/scenarios/command_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go index 000956bca..27165a368 100644 --- a/testing/scenarios/command_test.go +++ b/testing/scenarios/command_test.go @@ -33,7 +33,12 @@ func TestCommanderRemoveHandler(t *testing.T) { cmdPort := pickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ - serial.ToTypedMessage(&commander.Config{Tag: "api"}), + serial.ToTypedMessage(&commander.Config{ + Tag: "api", + Service: []*serial.TypedMessage{ + serial.ToTypedMessage(&command.Config{}), + }, + }), serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { @@ -42,7 +47,6 @@ func TestCommanderRemoveHandler(t *testing.T) { }, }, }), - serial.ToTypedMessage(&command.Config{}), }, Inbound: []*core.InboundHandlerConfig{ { From c1fc7c738a2305863ed07f444a49fec1ba65cf32 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Feb 2018 23:37:47 +0100 Subject: [PATCH 53/63] fix command listener --- app/commander/commander.go | 2 ++ app/commander/outbound.go | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/commander/commander.go b/app/commander/commander.go index 8b5deeee6..67a0b89b7 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -10,6 +10,7 @@ import ( "google.golang.org/grpc" "v2ray.com/core" "v2ray.com/core/common" + "v2ray.com/core/common/signal" ) type Commander struct { @@ -58,6 +59,7 @@ func (c *Commander) Start() error { listener := &OutboundListener{ buffer: make(chan net.Conn, 4), + done: signal.NewDone(), } go func() { diff --git a/app/commander/outbound.go b/app/commander/outbound.go index 9d9f36a57..92be34a9a 100644 --- a/app/commander/outbound.go +++ b/app/commander/outbound.go @@ -11,26 +11,39 @@ import ( type OutboundListener struct { buffer chan net.Conn + done *signal.Done } func (l *OutboundListener) add(conn net.Conn) { select { case l.buffer <- conn: + case <-l.done.C(): + conn.Close() default: conn.Close() } } func (l *OutboundListener) Accept() (net.Conn, error) { - c, open := <-l.buffer - if !open { - return nil, newError("listener closed") + select { + case <-l.done.C(): + return nil, newError("listern closed") + case c := <-l.buffer: + return c, nil } - return c, nil } func (l *OutboundListener) Close() error { - close(l.buffer) + l.done.Close() +L: + for { + select { + case c := <-l.buffer: + c.Close() + default: + break L + } + } return nil } From 87ba7dd0d152844c5f87274cbd2b16326b481a30 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 9 Feb 2018 11:32:12 +0100 Subject: [PATCH 54/63] implement remove user in vmess --- common/protocol/user_validator.go | 1 + proxy/vmess/inbound/inbound.go | 80 +++++++++++++++------ proxy/vmess/vmess.go | 112 +++++++++++++++++------------- proxy/vmess/vmess_test.go | 58 ++++++++++++++++ 4 files changed, 181 insertions(+), 70 deletions(-) create mode 100644 proxy/vmess/vmess_test.go diff --git a/common/protocol/user_validator.go b/common/protocol/user_validator.go index dfe779ed6..47816895c 100644 --- a/common/protocol/user_validator.go +++ b/common/protocol/user_validator.go @@ -3,4 +3,5 @@ package protocol type UserValidator interface { Add(user *User) error Get(timeHash []byte) (*User, Timestamp, bool) + Remove(email string) bool } diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 26deba018..5f32b1231 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -5,6 +5,7 @@ package inbound import ( "context" "io" + "strings" "sync" "time" @@ -25,7 +26,7 @@ import ( ) type userByEmail struct { - sync.RWMutex + sync.Mutex cache map[string]*protocol.User defaultLevel uint32 defaultAlterIDs uint16 @@ -34,7 +35,7 @@ type userByEmail struct { func newUserByEmail(users []*protocol.User, config *DefaultConfig) *userByEmail { cache := make(map[string]*protocol.User) for _, user := range users { - cache[user.Email] = user + cache[strings.ToLower(user.Email)] = user } return &userByEmail{ cache: cache, @@ -43,33 +44,59 @@ func newUserByEmail(users []*protocol.User, config *DefaultConfig) *userByEmail } } +func (v *userByEmail) addNoLock(u *protocol.User) bool { + email := strings.ToLower(u.Email) + user, found := v.cache[email] + if found { + return false + } + v.cache[email] = user + return true +} + +func (v *userByEmail) Add(u *protocol.User) bool { + v.Lock() + defer v.Unlock() + + return v.addNoLock(u) +} + func (v *userByEmail) Get(email string) (*protocol.User, bool) { - var user *protocol.User - var found bool - v.RLock() - user, found = v.cache[email] - v.RUnlock() + email = strings.ToLower(email) + + v.Lock() + defer v.Unlock() + + user, found := v.cache[email] if !found { - v.Lock() - user, found = v.cache[email] - if !found { - id := uuid.New() - account := &vmess.Account{ - Id: id.String(), - AlterId: uint32(v.defaultAlterIDs), - } - user = &protocol.User{ - Level: v.defaultLevel, - Email: email, - Account: serial.ToTypedMessage(account), - } - v.cache[email] = user + id := uuid.New() + account := &vmess.Account{ + Id: id.String(), + AlterId: uint32(v.defaultAlterIDs), } - v.Unlock() + user = &protocol.User{ + Level: v.defaultLevel, + Email: email, + Account: serial.ToTypedMessage(account), + } + v.cache[email] = user } return user, found } +func (v *userByEmail) Remove(email string) bool { + email = strings.ToLower(email) + + v.Lock() + defer v.Unlock() + + if _, found := v.cache[email]; !found { + return false + } + delete(v.cache, email) + return true +} + // Handler is an inbound connection handler that handles messages in VMess protocol. type Handler struct { policyManager core.PolicyManager @@ -129,11 +156,18 @@ func (h *Handler) GetUser(email string) *protocol.User { } func (h *Handler) AddUser(ctx context.Context, user *protocol.User) error { + if !h.usersByEmail.Add(user) { + return newError("User ", user.Email, " already exists.") + } return h.clients.Add(user) } func (h *Handler) RemoveUser(ctx context.Context, email string) error { - return newError("not implemented") + if !h.usersByEmail.Remove(email) { + return newError("User ", email, " not found.") + } + h.clients.Remove(email) + return nil } func transferRequest(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output ray.OutputStream) error { diff --git a/proxy/vmess/vmess.go b/proxy/vmess/vmess.go index 4c1cc5c35..52ead21f2 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -8,6 +8,7 @@ package vmess //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg vmess -path Proxy,VMess import ( + "strings" "sync" "time" @@ -21,34 +22,32 @@ const ( cacheDurationSec = 120 ) -type idEntry struct { - id *protocol.ID - userIdx int +type user struct { + user *protocol.User + account *InternalAccount lastSec protocol.Timestamp } type TimedUserValidator struct { sync.RWMutex - validUsers []*protocol.User - userHash map[[16]byte]indexTimePair - ids []*idEntry - hasher protocol.IDHash - baseTime protocol.Timestamp - task *signal.PeriodicTask + users []*user + userHash map[[16]byte]indexTimePair + hasher protocol.IDHash + baseTime protocol.Timestamp + task *signal.PeriodicTask } type indexTimePair struct { - index int + user *user timeInc uint32 } func NewTimedUserValidator(hasher protocol.IDHash) protocol.UserValidator { tuv := &TimedUserValidator{ - validUsers: make([]*protocol.User, 0, 16), - userHash: make(map[[16]byte]indexTimePair, 512), - ids: make([]*idEntry, 0, 512), - hasher: hasher, - baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*3), + users: make([]*user, 0, 16), + userHash: make(map[[16]byte]indexTimePair, 1024), + hasher: hasher, + baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*3), } tuv.task = &signal.PeriodicTask{ Interval: updateInterval, @@ -61,21 +60,27 @@ func NewTimedUserValidator(hasher protocol.IDHash) protocol.UserValidator { return tuv } -func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, idx int, entry *idEntry) { +func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, user *user) { var hashValue [16]byte - idHash := v.hasher(entry.id.Bytes()) - for entry.lastSec <= nowSec { - common.Must2(idHash.Write(entry.lastSec.Bytes(nil))) - idHash.Sum(hashValue[:0]) - idHash.Reset() + genHashForID := func(id *protocol.ID) { + idHash := v.hasher(id.Bytes()) + for ts := user.lastSec; ts <= nowSec; ts++ { + common.Must2(idHash.Write(ts.Bytes(nil))) + idHash.Sum(hashValue[:0]) + idHash.Reset() - v.userHash[hashValue] = indexTimePair{ - index: idx, - timeInc: uint32(entry.lastSec - v.baseTime), + v.userHash[hashValue] = indexTimePair{ + user: user, + timeInc: uint32(ts - v.baseTime), + } } - - entry.lastSec++ } + + genHashForID(user.account.ID) + for _, id := range user.account.AlterIDs { + genHashForID(id) + } + user.lastSec = nowSec } func (v *TimedUserValidator) removeExpiredHashes(expire uint32) { @@ -92,8 +97,8 @@ func (v *TimedUserValidator) updateUserHash() { v.Lock() defer v.Unlock() - for _, entry := range v.ids { - v.generateNewHashes(nowSec, entry.userIdx, entry) + for _, user := range v.users { + v.generateNewHashes(nowSec, user) } expire := protocol.Timestamp(now.Unix() - cacheDurationSec*3) @@ -102,13 +107,11 @@ func (v *TimedUserValidator) updateUserHash() { } } -func (v *TimedUserValidator) Add(user *protocol.User) error { +func (v *TimedUserValidator) Add(u *protocol.User) error { v.Lock() defer v.Unlock() - idx := len(v.validUsers) - v.validUsers = append(v.validUsers, user) - rawAccount, err := user.GetTypedAccount() + rawAccount, err := u.GetTypedAccount() if err != nil { return err } @@ -116,22 +119,13 @@ func (v *TimedUserValidator) Add(user *protocol.User) error { nowSec := time.Now().Unix() - entry := &idEntry{ - id: account.ID, - userIdx: idx, + uu := &user{ + user: u, + account: account, lastSec: protocol.Timestamp(nowSec - cacheDurationSec), } - v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry) - v.ids = append(v.ids, entry) - for _, alterid := range account.AlterIDs { - entry := &idEntry{ - id: alterid, - userIdx: idx, - lastSec: protocol.Timestamp(nowSec - cacheDurationSec), - } - v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry) - v.ids = append(v.ids, entry) - } + v.users = append(v.users, uu) + v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), uu) return nil } @@ -144,11 +138,35 @@ func (v *TimedUserValidator) Get(userHash []byte) (*protocol.User, protocol.Time copy(fixedSizeHash[:], userHash) pair, found := v.userHash[fixedSizeHash] if found { - return v.validUsers[pair.index], protocol.Timestamp(pair.timeInc) + v.baseTime, true + return pair.user.user, protocol.Timestamp(pair.timeInc) + v.baseTime, true } return nil, 0, false } +func (v *TimedUserValidator) Remove(email string) bool { + v.Lock() + defer v.Unlock() + + email = strings.ToLower(email) + idx := -1 + for i, u := range v.users { + if strings.ToLower(u.user.Email) == email { + idx = i + break + } + } + if idx == -1 { + return false + } + ulen := len(v.users) + if idx < len(v.users) { + v.users[idx] = v.users[ulen-1] + v.users[ulen-1] = nil + v.users = v.users[:ulen-1] + } + return true +} + // Close implements common.Closable. func (v *TimedUserValidator) Close() error { return v.task.Close() diff --git a/proxy/vmess/vmess_test.go b/proxy/vmess/vmess_test.go new file mode 100644 index 000000000..b3c805591 --- /dev/null +++ b/proxy/vmess/vmess_test.go @@ -0,0 +1,58 @@ +package vmess_test + +import ( + "testing" + "time" + + "v2ray.com/core/common" + "v2ray.com/core/common/serial" + "v2ray.com/core/common/uuid" + + "v2ray.com/core/common/protocol" + . "v2ray.com/core/proxy/vmess" + . "v2ray.com/ext/assert" +) + +func TestUserValidator(t *testing.T) { + assert := With(t) + + hasher := protocol.DefaultIDHash + v := NewTimedUserValidator(hasher) + defer common.Close(v) + + id := uuid.New() + user := &protocol.User{ + Email: "test", + Account: serial.ToTypedMessage(&Account{ + Id: id.String(), + AlterId: 8, + }), + } + common.Must(v.Add(user)) + + { + ts := protocol.Timestamp(time.Now().Unix()) + idHash := hasher(id.Bytes()) + idHash.Write(ts.Bytes(nil)) + userHash := idHash.Sum(nil) + + euser, ets, found := v.Get(userHash) + assert(found, IsTrue) + assert(euser.Email, Equals, user.Email) + assert(int64(ets), Equals, int64(ts)) + } + + { + ts := protocol.Timestamp(time.Now().Add(time.Second * 500).Unix()) + idHash := hasher(id.Bytes()) + idHash.Write(ts.Bytes(nil)) + userHash := idHash.Sum(nil) + + euser, _, found := v.Get(userHash) + assert(found, IsFalse) + assert(euser, IsNil) + } + + assert(v.Remove(user.Email), IsTrue) + assert(v.Remove(user.Email), IsFalse) +} From 05d93e5eb0235abfb526b83e80159bcb3bc36242 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 9 Feb 2018 11:43:23 +0100 Subject: [PATCH 55/63] fix handling for empty email addresses --- proxy/vmess/inbound/inbound.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 5f32b1231..68a3c93dd 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -32,13 +32,9 @@ type userByEmail struct { defaultAlterIDs uint16 } -func newUserByEmail(users []*protocol.User, config *DefaultConfig) *userByEmail { - cache := make(map[string]*protocol.User) - for _, user := range users { - cache[strings.ToLower(user.Email)] = user - } +func newUserByEmail(config *DefaultConfig) *userByEmail { return &userByEmail{ - cache: cache, + cache: make(map[string]*protocol.User), defaultLevel: config.Level, defaultAlterIDs: uint16(config.AlterId), } @@ -119,7 +115,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) { inboundHandlerManager: v.InboundHandlerManager(), clients: vmess.NewTimedUserValidator(protocol.DefaultIDHash), detours: config.Detour, - usersByEmail: newUserByEmail(config.User, config.GetDefaultValue()), + usersByEmail: newUserByEmail(config.GetDefaultValue()), sessionHistory: encoding.NewSessionHistory(), } @@ -156,13 +152,16 @@ func (h *Handler) GetUser(email string) *protocol.User { } func (h *Handler) AddUser(ctx context.Context, user *protocol.User) error { - if !h.usersByEmail.Add(user) { + if len(user.Email) > 0 && !h.usersByEmail.Add(user) { return newError("User ", user.Email, " already exists.") } return h.clients.Add(user) } func (h *Handler) RemoveUser(ctx context.Context, email string) error { + if len(email) == 0 { + return newError("Email must not be empty.") + } if !h.usersByEmail.Remove(email) { return newError("User ", email, " not found.") } From 8460d016ab4db845c49743076b66cc67bbd5cd38 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 9 Feb 2018 17:48:09 +0100 Subject: [PATCH 56/63] fix address parsing for mux --- proxy/vmess/encoding/encoding_test.go | 87 +++++++++++++++++++++++++ proxy/vmess/encoding/server.go | 93 ++++++++++++++++++--------- proxy/vmess/inbound/inbound.go | 5 -- proxy/vmess/outbound/outbound.go | 2 +- 4 files changed, 150 insertions(+), 37 deletions(-) diff --git a/proxy/vmess/encoding/encoding_test.go b/proxy/vmess/encoding/encoding_test.go index 794173921..2777f8cf3 100644 --- a/proxy/vmess/encoding/encoding_test.go +++ b/proxy/vmess/encoding/encoding_test.go @@ -66,3 +66,90 @@ func TestRequestSerialization(t *testing.T) { // anti replay attack assert(err, IsNotNil) } + +func TestInvalidRequest(t *testing.T) { + assert := With(t) + + user := &protocol.User{ + Level: 0, + Email: "test@v2ray.com", + } + id := uuid.New() + account := &vmess.Account{ + Id: id.String(), + AlterId: 0, + } + user.Account = serial.ToTypedMessage(account) + + expectedRequest := &protocol.RequestHeader{ + Version: 1, + User: user, + Command: protocol.RequestCommand(100), + Address: net.DomainAddress("www.v2ray.com"), + Port: net.Port(443), + Security: protocol.Security(protocol.SecurityType_AES128_GCM), + } + + buffer := buf.New() + client := NewClientSession(protocol.DefaultIDHash) + common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) + + buffer2 := buf.New() + buffer2.Append(buffer.Bytes()) + + sessionHistory := NewSessionHistory() + defer common.Close(sessionHistory) + + userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) + userValidator.Add(user) + defer common.Close(userValidator) + + server := NewServerSession(userValidator, sessionHistory) + _, err := server.DecodeRequestHeader(buffer) + assert(err, IsNotNil) +} + +func TestMuxRequest(t *testing.T) { + assert := With(t) + + user := &protocol.User{ + Level: 0, + Email: "test@v2ray.com", + } + id := uuid.New() + account := &vmess.Account{ + Id: id.String(), + AlterId: 0, + } + user.Account = serial.ToTypedMessage(account) + + expectedRequest := &protocol.RequestHeader{ + Version: 1, + User: user, + Command: protocol.RequestCommandMux, + Security: protocol.Security(protocol.SecurityType_AES128_GCM), + } + + buffer := buf.New() + client := NewClientSession(protocol.DefaultIDHash) + common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) + + buffer2 := buf.New() + buffer2.Append(buffer.Bytes()) + + sessionHistory := NewSessionHistory() + defer common.Close(sessionHistory) + + userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) + userValidator.Add(user) + defer common.Close(userValidator) + + server := NewServerSession(userValidator, sessionHistory) + actualRequest, err := server.DecodeRequestHeader(buffer) + assert(err, IsNil) + + assert(expectedRequest.Version, Equals, actualRequest.Version) + assert(byte(expectedRequest.Command), Equals, byte(actualRequest.Command)) + assert(byte(expectedRequest.Option), Equals, byte(actualRequest.Option)) + assert(byte(expectedRequest.Security), Equals, byte(actualRequest.Security)) +} diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index 8806540fb..d58914c1a 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -9,6 +9,8 @@ import ( "sync" "time" + "v2ray.com/core/common/dice" + "golang.org/x/crypto/chacha20poly1305" "v2ray.com/core/common" "v2ray.com/core/common/bitmask" @@ -103,6 +105,44 @@ func NewServerSession(validator protocol.UserValidator, sessionHistory *SessionH } } +func readAddress(buffer *buf.Buffer, reader io.Reader) (net.Address, net.Port, error) { + var address net.Address + var port net.Port + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 3)); err != nil { + return address, port, newError("failed to read port and address type").Base(err) + } + port = net.PortFromBytes(buffer.BytesRange(-3, -1)) + + addressType := protocol.AddressType(buffer.Byte(buffer.Len() - 1)) + switch addressType { + case protocol.AddressTypeIPv4: + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { + return address, port, newError("failed to read IPv4 address").Base(err) + } + address = net.IPAddress(buffer.BytesFrom(-4)) + case protocol.AddressTypeIPv6: + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil { + return address, port, newError("failed to read IPv6 address").Base(err) + } + address = net.IPAddress(buffer.BytesFrom(-16)) + case protocol.AddressTypeDomain: + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { + return address, port, newError("failed to read domain address").Base(err) + } + domainLength := int(buffer.Byte(buffer.Len() - 1)) + if domainLength == 0 { + return address, port, newError("zero length domain") + } + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { + return address, port, newError("failed to read domain address").Base(err) + } + address = net.DomainAddress(string(buffer.BytesFrom(-domainLength))) + default: + return address, port, newError("invalid address type", addressType) + } + return address, port, nil +} + func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.RequestHeader, error) { buffer := buf.New() defer buffer.Release() @@ -128,7 +168,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv) decryptor := crypto.NewCryptionReader(aesStream, reader) - if err := buffer.Reset(buf.ReadFullFrom(decryptor, 41)); err != nil { + if err := buffer.Reset(buf.ReadFullFrom(decryptor, 38)); err != nil { return nil, newError("failed to read request header").Base(err) } @@ -137,10 +177,6 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request Version: buffer.Byte(0), } - if request.Version != Version { - return nil, newError("invalid protocol version ", request.Version) - } - s.requestBodyIV = append([]byte(nil), buffer.BytesRange(1, 17)...) // 16 bytes s.requestBodyKey = append([]byte(nil), buffer.BytesRange(17, 33)...) // 16 bytes var sid sessionId @@ -159,33 +195,28 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request // 1 bytes reserved request.Command = protocol.RequestCommand(buffer.Byte(37)) - if request.Command != protocol.RequestCommandMux { - request.Port = net.PortFromBytes(buffer.BytesRange(38, 40)) - - switch protocol.AddressType(buffer.Byte(40)) { - case protocol.AddressTypeIPv4: - if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, 4)); err != nil { - return nil, newError("failed to read IPv4 address").Base(err) - } - request.Address = net.IPAddress(buffer.BytesFrom(-4)) - case protocol.AddressTypeIPv6: - if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, 16)); err != nil { - return nil, newError("failed to read IPv6 address").Base(err) - } - request.Address = net.IPAddress(buffer.BytesFrom(-16)) - case protocol.AddressTypeDomain: - if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, 1)); err != nil { - return nil, newError("failed to read domain address").Base(err) - } - domainLength := int(buffer.Byte(buffer.Len() - 1)) - if domainLength == 0 { - return nil, newError("zero length domain").Base(err) - } - if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, domainLength)); err != nil { - return nil, newError("failed to read domain address").Base(err) - } - request.Address = net.DomainAddress(string(buffer.BytesFrom(-domainLength))) + invalidRequest := false + switch request.Command { + case protocol.RequestCommandMux: + request.Address = net.DomainAddress("v1.mux.cool") + request.Port = 0 + case protocol.RequestCommandTCP, protocol.RequestCommandUDP: + if addr, port, err := readAddress(buffer, decryptor); err == nil { + request.Address = addr + request.Port = port + } else { + invalidRequest = true + newError("failed to read address").Base(err).WriteToLog() } + default: + invalidRequest = true + } + + if invalidRequest { + randomLen := dice.Roll(32) + // Read random number of bytes for prevent detection. + buffer.AppendSupplier(buf.ReadFullFrom(decryptor, randomLen)) + return nil, newError("invalid request") } if padingLen > 0 { diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 68a3c93dd..145a60ba2 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -239,11 +239,6 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i return err } - if request.Command == protocol.RequestCommandMux { - request.Address = net.DomainAddress("v1.mux.com") - request.Port = net.Port(0) - } - log.Record(&log.AccessMessage{ From: connection.RemoteAddr(), To: request.Destination(), diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index d195c8cc4..2d78184d7 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -75,7 +75,7 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial if target.Network == net.Network_UDP { command = protocol.RequestCommandUDP } - if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.com" { + if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" { command = protocol.RequestCommandMux } request := &protocol.RequestHeader{ From 30a0aa6fb0cf15cf5182c351d9908cbc30a377bd Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 9 Feb 2018 17:56:39 +0100 Subject: [PATCH 57/63] don't read 0 bytes --- proxy/vmess/encoding/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index d58914c1a..ea6bffa63 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -213,7 +213,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request } if invalidRequest { - randomLen := dice.Roll(32) + randomLen := dice.Roll(32) + 1 // Read random number of bytes for prevent detection. buffer.AppendSupplier(buf.ReadFullFrom(decryptor, randomLen)) return nil, newError("invalid request") From 42d83a703e246755db8a45f53e0296622f7be5e5 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 9 Feb 2018 22:29:30 +0100 Subject: [PATCH 58/63] fix transfer for mux --- common/protocol/headers.go | 9 ++++++--- proxy/vmess/encoding/client.go | 4 ++-- proxy/vmess/encoding/server.go | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/common/protocol/headers.go b/common/protocol/headers.go index 092de5eaa..b8fc58853 100644 --- a/common/protocol/headers.go +++ b/common/protocol/headers.go @@ -18,11 +18,14 @@ const ( ) func (c RequestCommand) TransferType() TransferType { - if c == RequestCommandTCP { + switch c { + case RequestCommandTCP, RequestCommandMux: + return TransferTypeStream + case RequestCommandUDP: + return TransferTypePacket + default: return TransferTypeStream } - - return TransferTypePacket } const ( diff --git a/proxy/vmess/encoding/client.go b/proxy/vmess/encoding/client.go index 8352a3eed..cc930dc51 100644 --- a/proxy/vmess/encoding/client.go +++ b/proxy/vmess/encoding/client.go @@ -131,7 +131,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write } if request.Security.Is(protocol.SecurityType_NONE) { if request.Option.Has(protocol.RequestOptionChunkStream) { - if request.Command == protocol.RequestCommandTCP { + if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamWriter(sizeParser, writer) } auth := &crypto.AEADAuthenticator{ @@ -236,7 +236,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read } if request.Security.Is(protocol.SecurityType_NONE) { if request.Option.Has(protocol.RequestOptionChunkStream) { - if request.Command == protocol.RequestCommandTCP { + if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamReader(sizeParser, reader) } diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index ea6bffa63..bd691db2d 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -252,7 +252,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade } if request.Security.Is(protocol.SecurityType_NONE) { if request.Option.Has(protocol.RequestOptionChunkStream) { - if request.Command == protocol.RequestCommandTCP { + if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamReader(sizeParser, reader) } @@ -338,7 +338,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ } if request.Security.Is(protocol.SecurityType_NONE) { if request.Option.Has(protocol.RequestOptionChunkStream) { - if request.Command == protocol.RequestCommandTCP { + if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamWriter(sizeParser, writer) } From 92aef24f98eec0361c6c9d7ea1246781e9046362 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 9 Feb 2018 23:07:38 +0100 Subject: [PATCH 59/63] remove unnecessary proxy functions --- app/proxyman/inbound/always.go | 8 +++++++- app/proxyman/outbound/handler.go | 9 ++++++++- proxy/handler_cache.go | 33 -------------------------------- 3 files changed, 15 insertions(+), 35 deletions(-) delete mode 100644 proxy/handler_cache.go diff --git a/app/proxyman/inbound/always.go b/app/proxyman/inbound/always.go index 7253fa5e1..d57e7b091 100644 --- a/app/proxyman/inbound/always.go +++ b/app/proxyman/inbound/always.go @@ -5,6 +5,7 @@ import ( "v2ray.com/core/app/proxyman" "v2ray.com/core/app/proxyman/mux" + "v2ray.com/core/common" "v2ray.com/core/common/dice" "v2ray.com/core/common/net" "v2ray.com/core/proxy" @@ -18,10 +19,14 @@ type AlwaysOnInboundHandler struct { } func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) { - p, err := proxy.CreateInboundHandler(ctx, proxyConfig) + rawProxy, err := common.CreateObject(ctx, proxyConfig) if err != nil { return nil, err } + p, ok := rawProxy.(proxy.Inbound) + if !ok { + return nil, newError("not an inbound proxy.") + } h := &AlwaysOnInboundHandler{ proxy: p, @@ -80,6 +85,7 @@ func (h *AlwaysOnInboundHandler) Close() error { for _, worker := range h.workers { worker.Close() } + h.mux.Close() return nil } diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index 98a1d979b..d3c237e16 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -7,6 +7,7 @@ import ( "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/app/proxyman/mux" + "v2ray.com/core/common" "v2ray.com/core/common/errors" "v2ray.com/core/common/net" "v2ray.com/core/proxy" @@ -50,11 +51,16 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (*Handl return nil, err } - proxyHandler, err := proxy.CreateOutboundHandler(ctx, proxyConfig) + rawProxyHandler, err := common.CreateObject(ctx, proxyConfig) if err != nil { return nil, err } + proxyHandler, ok := rawProxyHandler.(proxy.Outbound) + if !ok { + return nil, newError("not an outbound handler") + } + if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil && h.senderSettings.MultiplexSettings.Enabled { config := h.senderSettings.MultiplexSettings if config.Concurrency < 1 || config.Concurrency > 1024 { @@ -134,5 +140,6 @@ func (h *Handler) Start() error { // Close implements common.Runnable. func (h *Handler) Close() error { + common.Close(h.mux) return nil } diff --git a/proxy/handler_cache.go b/proxy/handler_cache.go deleted file mode 100644 index 0d580a720..000000000 --- a/proxy/handler_cache.go +++ /dev/null @@ -1,33 +0,0 @@ -package proxy - -import ( - "context" - - "v2ray.com/core/common" -) - -func CreateInboundHandler(ctx context.Context, config interface{}) (Inbound, error) { - handler, err := common.CreateObject(ctx, config) - if err != nil { - return nil, newError("failed to create inbound handler").Base(err) - } - switch h := handler.(type) { - case Inbound: - return h, nil - default: - return nil, newError("not a InboundHandler") - } -} - -func CreateOutboundHandler(ctx context.Context, config interface{}) (Outbound, error) { - handler, err := common.CreateObject(ctx, config) - if err != nil { - return nil, newError("failed to create outbound handler").Base(err) - } - switch h := handler.(type) { - case Outbound: - return h, nil - default: - return nil, newError("not a OutboundHandler") - } -} From c4f1e10a3d535a7bd5ea9c487af9d5bed7b357e5 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 10 Feb 2018 11:17:22 +0100 Subject: [PATCH 60/63] comments --- v2ray.go | 1 + 1 file changed, 1 insertion(+) diff --git a/v2ray.go b/v2ray.go index af710fd36..a95a14b5e 100644 --- a/v2ray.go +++ b/v2ray.go @@ -114,6 +114,7 @@ func (s *Instance) Close() error { } // Start starts the V2Ray instance, including all registered features. When Start returns error, the state of the instance is unknown. +// A V2Ray instance can be started only once. Upon closing, the instance is not guaranteed to start again. func (s *Instance) Start() error { s.access.Lock() defer s.access.Unlock() From 6f4bddd62e4a3237c27bea61ca79eb2c450e6e82 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 10 Feb 2018 11:17:40 +0100 Subject: [PATCH 61/63] disable mux command temporarily --- proxy/vmess/outbound/outbound.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index 2d78184d7..ec552090d 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -75,9 +75,9 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial if target.Network == net.Network_UDP { command = protocol.RequestCommandUDP } - if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" { - command = protocol.RequestCommandMux - } + //if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" { + // command = protocol.RequestCommandMux + //} request := &protocol.RequestHeader{ Version: encoding.Version, User: rec.PickUser(), From 0a6a9547a0c33be1aa42cd89601d82767d8284a3 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 11 Feb 2018 02:06:43 +0100 Subject: [PATCH 62/63] fix kcp for not sending data immediately --- transport/internet/kcp/connection.go | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/transport/internet/kcp/connection.go b/transport/internet/kcp/connection.go index eac7a6f43..c7f40642f 100644 --- a/transport/internet/kcp/connection.go +++ b/transport/internet/kcp/connection.go @@ -338,10 +338,15 @@ func (c *Connection) waitForDataOutput() error { // Write implements io.Writer. func (c *Connection) Write(b []byte) (int, error) { - totalWritten := 0 + updatePending := false + defer func() { + if updatePending { + c.dataUpdater.WakeUp() + } + }() for { - dataWritten := false + totalWritten := 0 for { if c == nil || c.State() != StateActive { return totalWritten, io.ErrClosedPipe @@ -354,15 +359,16 @@ func (c *Connection) Write(b []byte) (int, error) { break } - dataWritten = true + updatePending = true if totalWritten == len(b) { return totalWritten, nil } } - if dataWritten { + if updatePending { c.dataUpdater.WakeUp() + updatePending = false } if err := c.waitForDataOutput(); err != nil { @@ -375,8 +381,14 @@ func (c *Connection) Write(b []byte) (int, error) { func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error { defer mb.Release() + updatePending := false + defer func() { + if updatePending { + c.dataUpdater.WakeUp() + } + }() + for { - dataWritten := false for { if c == nil || c.State() != StateActive { return io.ErrClosedPipe @@ -387,14 +399,15 @@ func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error { }) { break } - dataWritten = true + updatePending = true if mb.IsEmpty() { return nil } } - if dataWritten { + if updatePending { c.dataUpdater.WakeUp() + updatePending = false } if err := c.waitForDataOutput(); err != nil { From ae52e325a243af58d3bf36686dec0e8b40d85894 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 11 Feb 2018 02:08:20 +0100 Subject: [PATCH 63/63] send ack more frequently too --- transport/internet/kcp/connection.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/transport/internet/kcp/connection.go b/transport/internet/kcp/connection.go index c7f40642f..fd0396052 100644 --- a/transport/internet/kcp/connection.go +++ b/transport/internet/kcp/connection.go @@ -258,6 +258,7 @@ func (c *Connection) ReadMultiBuffer() (buf.MultiBuffer, error) { } mb := c.receivingWorker.ReadMultiBuffer() if !mb.IsEmpty() { + c.dataUpdater.WakeUp() return mb, nil } @@ -307,6 +308,7 @@ func (c *Connection) Read(b []byte) (int, error) { } nBytes := c.receivingWorker.Read(b) if nBytes > 0 { + c.dataUpdater.WakeUp() return nBytes, nil }