diff --git a/.travis.yml b/.travis.yml index df4b91fa..e5f3318f 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/app/api/api.go b/app/api/api.go deleted file mode 100644 index 21e27890..00000000 --- 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 31abfb09..00000000 --- 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 -} diff --git a/app/app.go b/app/app.go index c688df1e..4879f7a4 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/commander/commander.go b/app/commander/commander.go new file mode 100644 index 00000000..67a0b89b --- /dev/null +++ b/app/commander/commander.go @@ -0,0 +1,95 @@ +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" + "v2ray.com/core/common" + "v2ray.com/core/common/signal" +) + +type Commander struct { + sync.Mutex + server *grpc.Server + config Config + v *core.Instance + ohm core.OutboundHandlerManager +} + +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(), + v: v, + } + if err := v.RegisterFeature((*core.Commander)(nil), c); err != nil { + return nil, err + } + return c, nil +} + +func (c *Commander) Start() error { + c.Lock() + c.server = grpc.NewServer() + 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() + + listener := &OutboundListener{ + buffer: make(chan net.Conn, 4), + done: signal.NewDone(), + } + + 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{ + tag: c.config.Tag, + listener: listener, + }) + return nil +} + +func (c *Commander) Close() error { + c.Lock() + defer c.Unlock() + + if c.server != nil { + c.server.Stop() + c.server = nil + } + + return 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/commander/config.pb.go b/app/commander/config.pb.go new file mode 100644 index 00000000..0ba805e5 --- /dev/null +++ b/app/commander/config.pb.go @@ -0,0 +1,68 @@ +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 +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 + +// 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{} } +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 (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") +} + +func init() { proto.RegisterFile("v2ray.com/core/app/commander/config.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 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, 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 new file mode 100644 index 00000000..e6aaa7ec --- /dev/null +++ b/app/commander/config.proto @@ -0,0 +1,17 @@ +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; + +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/dispatcher/impl/errors.generated.go b/app/commander/errors.generated.go similarity index 54% rename from app/dispatcher/impl/errors.generated.go rename to app/commander/errors.generated.go index 885ba26d..70edc708 100644 --- a/app/dispatcher/impl/errors.generated.go +++ b/app/commander/errors.generated.go @@ -1,7 +1,7 @@ -package impl +package commander import "v2ray.com/core/common/errors" func newError(values ...interface{}) *errors.Error { - return errors.New(values...).Path("App", "Dispatcher", "Default") + return errors.New(values...).Path("App", "Commander") } diff --git a/app/commander/outbound.go b/app/commander/outbound.go new file mode 100644 index 00000000..92be34a9 --- /dev/null +++ b/app/commander/outbound.go @@ -0,0 +1,101 @@ +package commander + +import ( + "context" + "net" + "sync" + + "v2ray.com/core/common/signal" + "v2ray.com/core/transport/ray" +) + +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) { + select { + case <-l.done.C(): + return nil, newError("listern closed") + case c := <-l.buffer: + return c, nil + } +} + +func (l *OutboundListener) Close() error { + l.done.Close() +L: + for { + select { + case c := <-l.buffer: + c.Close() + default: + break L + } + } + 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 + 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 +} + +func (co *CommanderOutbound) Tag() string { + return co.tag +} + +func (co *CommanderOutbound) Start() error { + co.access.Lock() + co.closed = false + co.access.Unlock() + return nil +} + +func (co *CommanderOutbound) Close() error { + co.access.Lock() + co.closed = true + co.listener.Close() + co.access.Unlock() + + return nil +} diff --git a/app/commander/service.go b/app/commander/service.go new file mode 100644 index 00000000..fb9340db --- /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/dispatcher/impl/default.go b/app/dispatcher/default.go similarity index 69% rename from app/dispatcher/impl/default.go rename to app/dispatcher/default.go index 22cc0f59..6be84fcf 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 } @@ -55,14 +49,9 @@ func (*DefaultDispatcher) Start() error { } // Close implements app.Application. -func (*DefaultDispatcher) Close() {} +func (*DefaultDispatcher) Close() error { return nil } -// 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 67b6486f..83888b94 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 00000000..424bd981 --- /dev/null +++ b/app/dispatcher/errors.generated.go @@ -0,0 +1,7 @@ +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/sniffer.go b/app/dispatcher/sniffer.go similarity index 99% rename from app/dispatcher/impl/sniffer.go rename to app/dispatcher/sniffer.go index ba4add25..a02d8105 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 9ae7eac8..1ac11f1b 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 785739ea..a689e785 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), @@ -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 diff --git a/app/dns/server.go b/app/dns/server.go index d8494e45..2972ff5c 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,10 +8,10 @@ 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" + "v2ray.com/core/common/signal" ) const ( @@ -38,56 +38,60 @@ 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) { - 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) - } + 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.") + } + + 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 } -func (*Server) Interface() interface{} { - return (*Server)(nil) -} - +// Start implements common.Runnable. func (s *Server) Start() error { - net.RegisterIPResolver(s) - return nil + return s.task.Start() } -func (*Server) Close() { - net.RegisterIPResolver(net.SystemIPResolver()) +// Close implements common.Runnable. +func (s *Server) Close() error { + return s.task.Close() } func (s *Server) GetCached(domain string) []net.IP { @@ -101,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) } } @@ -129,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 { diff --git a/app/dns/server_test.go b/app/dns/server_test.go index d773f827..5fd1bc92 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/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{ + { + 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/log/log.go b/app/log/log.go index 9518f722..c2dc5e57 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: @@ -108,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/config.go b/app/policy/config.go index 350d0751..4d2f9452 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 3931523b..3798ce91 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 00000000..14f7b7b9 --- /dev/null +++ b/app/policy/manager.go @@ -0,0 +1,62 @@ +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() error { + return nil +} + +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 e84c462b..00000000 --- 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 f67e736e..32554afe 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/command/command.go b/app/proxyman/command/command.go new file mode 100644 index 00000000..c0357579 --- /dev/null +++ b/app/proxyman/command/command.go @@ -0,0 +1,148 @@ +package command + +import ( + "context" + + grpc "google.golang.org/grpc" + "v2ray.com/core" + "v2ray.com/core/common" + "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 getInbound(handler core.InboundHandler) (proxy.Inbound, error) { + gi, ok := handler.(proxy.GetInbound) + if !ok { + 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 + } + um, ok := p.(proxy.UserManager) + if !ok { + return newError("proxy is not an UserManager") + } + return um.AddUser(ctx, op.User) +} + +// ApplyInbound implements InboundOperation. +func (op *RemoveUserOperation) ApplyInbound(ctx context.Context, handler core.InboundHandler) error { + p, err := getInbound(handler) + if err != nil { + return err + } + um, ok := p.(proxy.UserManager) + if !ok { + return newError("proxy is not an UserManager") + } + return um.RemoveUser(ctx, op.Email) +} + +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 service struct { + v *core.Instance +} + +func (s *service) Register(server *grpc.Server) { + RegisterHandlerServiceServer(server, &handlerServer{ + s: s.v, + ihm: s.v.InboundHandlerManager(), + ohm: s.v.OutboundHandlerManager(), + }) +} + +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.") + } + return &service{v: s}, nil + })) +} diff --git a/app/proxyman/command/command.pb.go b/app/proxyman/command/command.pb.go new file mode 100644 index 00000000..fe0c1071 --- /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 00000000..c40c8ec2 --- /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 00000000..8c18efaf --- /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 00000000..7c196cf5 --- /dev/null +++ b/app/proxyman/command/errors.generated.go @@ -0,0 +1,7 @@ +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/config.go b/app/proxyman/config.go index 9eae4e3f..6f20ba48 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 51df2860..3a64376e 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 6219db4f..15f2171e 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/inbound/always.go b/app/proxyman/inbound/always.go index 4d20739f..d57e7b09 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" @@ -14,17 +15,23 @@ 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) { - 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, mux: mux.NewServer(ctx), + tag: tag, } nl := p.Network() @@ -74,16 +81,26 @@ func (h *AlwaysOnInboundHandler) Start() error { return nil } -func (h *AlwaysOnInboundHandler) Close() { +func (h *AlwaysOnInboundHandler) Close() error { for _, worker := range h.workers { worker.Close() } + h.mux.Close() + return nil } -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 +} + +func (h *AlwaysOnInboundHandler) GetInbound() proxy.Inbound { + return h.proxy +} diff --git a/app/proxyman/inbound/dynamic.go b/app/proxyman/inbound/dynamic.go index ca1aa9d0..e6f77972 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,36 +141,22 @@ 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() (proxy.Inbound, net.Port, int) { +func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) { h.workerMutex.RLock() defer h.workerMutex.RUnlock() @@ -174,3 +167,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 bedf07b2..e7a190bb 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -4,65 +4,60 @@ package inbound import ( "context" + "sync" + "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 + access sync.RWMutex + untaggedHandler []core.InboundHandler + taggedHandlers map[string]core.InboundHandler + running bool } +// New returns a new Manager for inbound handlers. 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 - } +// AddHandler implements core.InboundHandlerManager. +func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) error { + m.access.Lock() + defer m.access.Unlock() - if handler == nil { - return newError("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError() - } - - m.handlers = append(m.handlers, handler) + tag := handler.Tag() if len(tag) > 0 { m.taggedHandlers[tag] = handler + } else { + m.untaggedHandler = append(m.untaggedHandler, handler) } + + if m.running { + return handler.Start() + } + return nil } -func (m *Manager) GetHandler(ctx context.Context, tag string) (proxyman.InboundHandler, error) { +// GetHandler returns core.InboundHandlerManager. +func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) { + m.access.RLock() + defer m.access.RUnlock() + handler, found := m.taggedHandlers[tag] if !found { return nil, newError("handler not found: ", tag) @@ -70,8 +65,36 @@ func (m *Manager) GetHandler(ctx context.Context, tag string) (proxyman.InboundH return handler, nil } +func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { + if len(tag) == 0 { + return core.ErrNoClue + } + + m.access.Lock() + defer m.access.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 { + m.access.Lock() + defer m.access.Unlock() + + m.running = true + + 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 } @@ -79,18 +102,52 @@ func (m *Manager) Start() error { return nil } -func (m *Manager) Close() { - for _, handler := range m.handlers { +func (m *Manager) Close() error { + m.access.Lock() + defer m.access.Unlock() + + m.running = false + + for _, handler := range m.taggedHandlers { handler.Close() } + for _, handler := range m.untaggedHandler { + handler.Close() + } + + return nil } -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 6fce64c8..804092fe 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -7,10 +7,12 @@ import ( "sync/atomic" "time" - "v2ray.com/core/app/dispatcher" + "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 } @@ -31,16 +33,14 @@ type tcpWorker struct { stream *internet.StreamConfig recvOrigDest bool tag string - dispatcher dispatcher.Interface + 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,7 +100,7 @@ type udpConn struct { output func([]byte) (int, error) remote net.Addr local net.Addr - cancel context.CancelFunc + done *signal.Done } func (c *udpConn) updateActivity() { @@ -129,13 +108,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.done.C(): return 0, io.EOF } - defer in.Release() - c.updateActivity() - return copy(buf, in.Bytes()), nil } // Write implements io.Writer. @@ -148,6 +128,7 @@ func (c *udpConn) Write(buf []byte) (int, error) { } func (c *udpConn) Close() error { + common.Close(c.done) return nil } @@ -171,7 +152,7 @@ func (*udpConn) SetWriteDeadline(time.Time) error { return nil } -type connId struct { +type connID struct { src net.Destination dest net.Destination } @@ -185,14 +166,13 @@ type udpWorker struct { port net.Port recvOrigDest bool tag string - dispatcher dispatcher.Interface + dispatcher core.Dispatcher - ctx context.Context - cancel context.CancelFunc - activeConn map[connId]*udpConn + done *signal.Done + 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() @@ -213,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 @@ -221,22 +202,22 @@ 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, } 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.cancel = cancel + ctx := context.Background() if originalDest.IsValid() { ctx = proxy.ContextWithOriginalTarget(ctx, originalDest) } @@ -248,23 +229,21 @@ 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() }() } } -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) - ctx, cancel := context.WithCancel(context.Background()) - w.ctx = ctx - w.cancel = cancel + w.activeConn = make(map[connID]*udpConn, 16) + w.done = signal.NewDone() h, err := udp.ListenUDP(w.address, w.port, udp.ListenOption{ Callback: w.callback, ReceiveOriginalDest: w.recvOrigDest, @@ -277,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() { @@ -290,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() @@ -298,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 ca34c34c..b921dbd6 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -8,13 +8,13 @@ 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" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" + "v2ray.com/core/common/signal" "v2ray.com/core/proxy" "v2ray.com/core/transport/ray" ) @@ -75,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 } @@ -86,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 @@ -113,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() { @@ -129,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() @@ -137,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 } } } @@ -171,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() @@ -227,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()) @@ -262,21 +255,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 +281,16 @@ 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() error { + return nil +} + type ServerWorker struct { - dispatcher dispatcher.Interface + dispatcher core.Dispatcher outboundRay ray.OutboundRay sessionManager *SessionManager } diff --git a/app/proxyman/mux/session.go b/app/proxyman/mux/session.go index 66e1b729..c828d21c 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 64c290bd..f8cbc21f 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/handler.go b/app/proxyman/outbound/handler.go index 33e06d5e..d3c237e1 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -3,12 +3,11 @@ package outbound import ( "context" "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" + "v2ray.com/core/common" "v2ray.com/core/common/errors" "v2ray.com/core/common/net" "v2ray.com/core/proxy" @@ -17,29 +16,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,11 +46,21 @@ 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 } + 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 { @@ -71,6 +73,11 @@ func NewHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) (*H return h, nil } +// Tag implements core.OutboundHandler. +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 { @@ -103,7 +110,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.InboundOutput(), stream.InboundInput()), nil } newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog() @@ -121,98 +128,18 @@ 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 +// GetOutbound implements proxy.GetOutbound. +func (h *Handler) GetOutbound() proxy.Outbound { + return h.proxy } -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() +// Start implements common.Runnable. +func (h *Handler) Start() error { 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 { +// Close implements common.Runnable. +func (h *Handler) Close() error { + common.Close(h.mux) return nil } diff --git a/app/proxyman/outbound/handler_test.go b/app/proxyman/outbound/handler_test.go new file mode 100644 index 00000000..bd15e034 --- /dev/null +++ b/app/proxyman/outbound/handler_test.go @@ -0,0 +1,16 @@ +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)) + assert((*Manager)(nil), Implements, (*core.OutboundHandlerManager)(nil)) +} diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 5835cb0e..52848f32 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -6,67 +6,130 @@ import ( "context" "sync" + "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/common" ) // Manager is to manage all outbound handlers. type Manager struct { - sync.RWMutex - defaultHandler *Handler - taggedHandler map[string]*Handler + access sync.RWMutex + defaultHandler core.OutboundHandler + taggedHandler map[string]core.OutboundHandler + untaggedHandlers []core.OutboundHandler + running bool } // New creates a new Manager. func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) { - return &Manager{ - taggedHandler: make(map[string]*Handler), - }, 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 } -// Interface implements Application.Interface. -func (*Manager) Interface() interface{} { - return (*proxyman.OutboundHandlerManager)(nil) +// Start implements core.Feature +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 } -// Start implements Application.Start -func (*Manager) Start() error { return nil } +// Close implements core.Feature +func (m *Manager) Close() error { + m.access.Lock() + defer m.access.Unlock() -// Close implements Application.Close -func (*Manager) Close() {} + 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 { + m.access.RLock() + defer m.access.RUnlock() -func (m *Manager) GetDefaultHandler() proxyman.OutboundHandler { - m.RLock() - defer m.RUnlock() if m.defaultHandler == nil { return nil } return m.defaultHandler } -func (m *Manager) GetHandler(tag string) proxyman.OutboundHandler { - m.RLock() - defer m.RUnlock() +// GetHandler implements core.OutboundHandlerManager. +func (m *Manager) GetHandler(tag string) core.OutboundHandler { + m.access.RLock() + defer m.access.RUnlock() if handler, found := m.taggedHandler[tag]; found { return handler } return nil } -func (m *Manager) AddHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) error { - m.Lock() - defer m.Unlock() +// AddHandler implements core.OutboundHandlerManager. +func (m *Manager) AddHandler(ctx context.Context, handler core.OutboundHandler) error { + m.access.Lock() + defer m.access.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 + } else { + m.untaggedHandlers = append(m.untaggedHandlers, handler) + } + + if m.running { + return handler.Start() + } + + return nil +} + +// RemoveHandler implements core.OutboundHandlerManager. +func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { + if len(tag) == 0 { + return core.ErrNoClue + } + m.access.Lock() + defer m.access.Unlock() + + delete(m.taggedHandler, tag) + if m.defaultHandler.Tag() == tag { + m.defaultHandler = nil } return nil @@ -76,4 +139,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 f782a77b..1ae393aa 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 17184a2d..5e0109d5 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,25 +107,15 @@ 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 { return nil } -func (*Router) Close() {} - -func FromSpace(space app.Space) *Router { - app := space.GetApplication((*Router)(nil)) - if app == nil { - return nil - } - return app.(*Router) +func (*Router) Close() error { + return nil } func init() { diff --git a/app/router/router_test.go b/app/router/router_test.go index 855272cb..856d5adb 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 4a7bc22c..00000000 --- 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/clock.go b/clock.go new file mode 100644 index 00000000..54bcb905 --- /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() error { + c.RLock() + defer c.RUnlock() + + if c.Clock == nil { + return nil + } + + return c.Clock.Close() +} + +func (c *syncClock) Set(clock Clock) { + c.Lock() + defer c.Unlock() + + c.Clock = clock +} diff --git a/commander.go b/commander.go new file mode 100644 index 00000000..eb2d3750 --- /dev/null +++ b/commander.go @@ -0,0 +1,44 @@ +package core + +import ( + "sync" +) + +// Commander is a feature that accepts commands from external source. +type Commander interface { + Feature +} + +type syncCommander struct { + sync.RWMutex + Commander +} + +func (c *syncCommander) Start() error { + c.RLock() + defer c.RUnlock() + + if c.Commander == nil { + return nil + } + + return c.Commander.Start() +} + +func (c *syncCommander) Close() error { + c.RLock() + defer c.RUnlock() + + if c.Commander == nil { + return nil + } + + return c.Commander.Close() +} + +func (c *syncCommander) Set(commander Commander) { + c.Lock() + defer c.Unlock() + + c.Commander = commander +} diff --git a/common/event/event.go b/common/event/event.go deleted file mode 100644 index 36a58db5..00000000 --- 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 -} diff --git a/common/interfaces.go b/common/interfaces.go new file mode 100644 index 00000000..d5cc177e --- /dev/null +++ b/common/interfaces.go @@ -0,0 +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 + + Closable +} diff --git a/common/log/log.go b/common/log/log.go index fda2431c..c7cdfadf 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 } diff --git a/common/net/dns.go b/common/net/dns.go deleted file mode 100644 index 52ef7436..00000000 --- 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 33dc86b6..b2ffbf2f 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 diff --git a/common/protocol/headers.go b/common/protocol/headers.go index 1304a7e4..b8fc5885 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 ( @@ -79,7 +82,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 4360a563..e6b0176e 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/protocol/user.pb.go b/common/protocol/user.pb.go index 46c68a5f..882c1542 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/protocol/user_validator.go b/common/protocol/user_validator.go index dfe779ed..47816895 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/common/router/dispatcher.go b/common/router/dispatcher.go new file mode 100644 index 00000000..4934d302 --- /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 00000000..a2dc15bb --- /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/common/signal/done.go b/common/signal/done.go new file mode 100644 index 00000000..8cffb62e --- /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 0d98c220..a4c4d5b5 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 034a4ee7..f9a80db5 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 00000000..9d876804 --- /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/task_test.go b/common/signal/task_test.go new file mode 100644 index 00000000..75be9742 --- /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) +} diff --git a/common/signal/timer.go b/common/signal/timer.go index 16482101..6a63af05 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -10,23 +10,30 @@ type ActivityUpdater interface { } type ActivityTimer struct { - updated chan bool + updated chan struct{} timeout chan time.Duration + closing chan struct{} } func (t *ActivityTimer) Update() { select { - case t.updated <- true: + case t.updated <- struct{}{}: default: } } 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 { @@ -65,7 +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), + updated: make(chan struct{}, 1), + closing: make(chan struct{}), } timer.timeout <- timeout go timer.run(ctx, cancel) diff --git a/common/signal/timer_test.go b/common/signal/timer_test.go index a34cfbc1..f299b308 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) +} diff --git a/common/uuid/uuid.go b/common/uuid/uuid.go index 08225b5d..0e0ca386 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 f8b90085..2e7bad4f 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/config.pb.go b/config.pb.go index 7c3426a5..1429f6e2 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 b5e337fc..6101c0fa 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 00000000..c92f0bac --- /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/core.go b/core.go index 410e9e5c..52993789 100644 --- a/core.go +++ b/core.go @@ -18,7 +18,7 @@ import ( ) var ( - version = "3.6" + version = "3.8" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." diff --git a/dial.go b/dial.go new file mode 100644 index 00000000..1819bcaf --- /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.InboundOutput(), r.InboundInput()), nil +} diff --git a/dns.go b/dns.go new file mode 100644 index 00000000..5e94d1c2 --- /dev/null +++ b/dns.go @@ -0,0 +1,59 @@ +package core + +import ( + "net" + "sync" + + "v2ray.com/core/common" +) + +// 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() error { + d.RLock() + defer d.RUnlock() + + return common.Close(d.DNSClient) +} + +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 85380d04..21a90716 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/dispatcher/impl" + _ "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/manager" + _ "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" diff --git a/network.go b/network.go new file mode 100644 index 00000000..d526a91f --- /dev/null +++ b/network.go @@ -0,0 +1,168 @@ +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 { + common.Runnable + 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 + + // RemoveHandler removes a handler from InboundHandlerManager. + RemoveHandler(ctx context.Context, tag string) 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() error { + m.RLock() + defer m.RUnlock() + + return common.Close(m.InboundHandlerManager) +} + +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 + + // RemoveHandler removes a handler from OutboundHandlerManager. + RemoveHandler(ctx context.Context, tag string) 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() error { + m.RLock() + defer m.RUnlock() + + return common.Close(m.OutboundHandlerManager) +} + +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 00000000..a0717320 --- /dev/null +++ b/policy.go @@ -0,0 +1,117 @@ +package core + +import ( + "sync" + "time" + + "v2ray.com/core/common" +) + +// 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() error { + m.RLock() + defer m.RUnlock() + + return common.Close(m.PolicyManager) +} + +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 f56901d5..80e95239 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 c7294a52..ef87340f 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/handler_cache.go b/proxy/handler_cache.go deleted file mode 100644 index 0d580a72..00000000 --- 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") - } -} diff --git a/proxy/http/server.go b/proxy/http/server.go index 6841bfe7..9179de13 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 ea359f63..7174c245 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -10,8 +10,9 @@ package proxy import ( "context" - "v2ray.com/core/app/dispatcher" + "v2ray.com/core" "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" "v2ray.com/core/transport/internet" "v2ray.com/core/transport/ray" ) @@ -22,7 +23,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. @@ -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 +} diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index 3d5d2be4..3ce880c6 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 { @@ -164,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 54de878f..4d67b02b 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() <= c.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())) diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index afb1812f..31b345a0 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 6c667046..262ef4ad 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/account.go b/proxy/vmess/account.go index 44c2908d..57cc1c5a 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/encoding/client.go b/proxy/vmess/encoding/client.go index 8352a3ee..cc930dc5 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/encoding_test.go b/proxy/vmess/encoding/encoding_test.go index 6ed0edc1..2777f8cf 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" @@ -22,8 +21,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) @@ -44,11 +44,12 @@ func TestRequestSerialization(t *testing.T) { buffer2 := buf.New() buffer2.Append(buffer.Bytes()) - ctx, cancel := context.WithCancel(context.Background()) - sessionHistory := NewSessionHistory(ctx) + sessionHistory := NewSessionHistory() + defer common.Close(sessionHistory) - 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) @@ -64,6 +65,91 @@ func TestRequestSerialization(t *testing.T) { _, err = server.DecodeRequestHeader(buffer2) // anti replay attack assert(err, IsNotNil) - - cancel() +} + +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 0859ed82..bd691db2 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" @@ -10,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" @@ -31,29 +32,34 @@ type sessionId struct { type SessionHistory struct { sync.RWMutex cache map[sessionId]time.Time - token *signal.Semaphore - ctx context.Context + task *signal.PeriodicTask } -func NewSessionHistory(ctx context.Context) *SessionHistory { +func NewSessionHistory() *SessionHistory { h := &SessionHistory{ cache: make(map[sessionId]time.Time, 128), - token: signal.NewSemaphore(1), - ctx: ctx, } + 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() - h.cache[session] = time.Now().Add(time.Minute * 3) - h.Unlock() + defer h.Unlock() - select { - case <-h.token.Wait(): - go h.run() - default: - } + h.cache[session] = time.Now().Add(time.Minute * 3) } func (h *SessionHistory) has(session sessionId) bool { @@ -66,31 +72,16 @@ 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() } } @@ -114,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() @@ -139,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) } @@ -148,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 @@ -170,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) + 1 + // Read random number of bytes for prevent detection. + buffer.AppendSupplier(buf.ReadFullFrom(decryptor, randomLen)) + return nil, newError("invalid request") } if padingLen > 0 { @@ -232,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) } @@ -318,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) } diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 4b310f6c..145a60ba 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -5,13 +5,11 @@ package inbound import ( "context" "io" + "strings" "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" @@ -28,96 +26,116 @@ import ( ) type userByEmail struct { - sync.RWMutex + sync.Mutex cache map[string]*protocol.User defaultLevel uint32 defaultAlterIDs uint16 } -func newUserByEmail(users []*protocol.User, config *DefaultConfig) *userByEmail { - cache := make(map[string]*protocol.User) - for _, user := range users { - cache[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), } } +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 { - account := &vmess.Account{ - Id: uuid.New().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 { - 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") + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context.") + } + + handler := &Handler{ + policyManager: v.PolicyManager(), + inboundHandlerManager: v.InboundHandlerManager(), + clients: vmess.NewTimedUserValidator(protocol.DefaultIDHash), + detours: config.Detour, + usersByEmail: newUserByEmail(config.GetDefaultValue()), + sessionHistory: encoding.NewSessionHistory(), } - allowedClients := vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash) for _, user := range config.User { - if err := allowedClients.Add(user); err != nil { + if err := handler.AddUser(ctx, user); err != nil { return nil, newError("failed to initiate user").Base(err) } } - handler := &Handler{ - clients: allowedClients, - detours: config.Detour, - usersByEmail: newUserByEmail(config.User, config.GetDefaultValue()), - sessionHistory: encoding.NewSessionHistory(ctx), - } - - 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 - }) - 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{ @@ -133,6 +151,24 @@ func (h *Handler) GetUser(email string) *protocol.User { return user } +func (h *Handler) AddUser(ctx context.Context, user *protocol.User) error { + 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.") + } + h.clients.Remove(email) + return nil +} + func transferRequest(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output ray.OutputStream) error { defer output.Close() @@ -179,9 +215,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() } @@ -198,16 +234,11 @@ 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 } - 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(), @@ -221,11 +252,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 +266,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), @@ -265,7 +296,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() diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index 5cc5f818..ec552090 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 } @@ -85,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.com" { - 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(), @@ -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/proxy/vmess/vmess.go b/proxy/vmess/vmess.go index d40f77f7..52ead21f 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -8,99 +8,110 @@ package vmess //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg vmess -path Proxy,VMess import ( - "context" + "strings" "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 { - id *protocol.ID - userIdx int - lastSec protocol.Timestamp - lastSecRemoval protocol.Timestamp +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 + 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(ctx context.Context, hasher protocol.IDHash) protocol.UserValidator { - tus := &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), +func NewTimedUserValidator(hasher protocol.IDHash) protocol.UserValidator { + tuv := &TimedUserValidator{ + users: make([]*user, 0, 16), + userHash: make(map[[16]byte]indexTimePair, 1024), + 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) { +func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, user *user) { 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() + 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() - 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) 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) + v.userHash[hashValue] = indexTimePair{ + user: user, + timeInc: uint32(ts - v.baseTime), } - v.Unlock() - case <-ctx.Done(): - return + } + } + + genHashForID(user.account.ID) + for _, id := range user.account.AlterIDs { + genHashForID(id) + } + user.lastSec = nowSec +} + +func (v *TimedUserValidator) removeExpiredHashes(expire uint32) { + for key, pair := range v.userHash { + if pair.timeInc < expire { + delete(v.userHash, key) } } } -func (v *TimedUserValidator) Add(user *protocol.User) error { +func (v *TimedUserValidator) updateUserHash() { + now := time.Now() + nowSec := protocol.Timestamp(now.Unix() + cacheDurationSec) v.Lock() defer v.Unlock() - idx := len(v.validUsers) - v.validUsers = append(v.validUsers, user) - rawAccount, err := user.GetTypedAccount() + for _, user := range v.users { + v.generateNewHashes(nowSec, user) + } + + expire := protocol.Timestamp(now.Unix() - cacheDurationSec*3) + if expire > v.baseTime { + v.removeExpiredHashes(uint32(expire - v.baseTime)) + } +} + +func (v *TimedUserValidator) Add(u *protocol.User) error { + v.Lock() + defer v.Unlock() + + rawAccount, err := u.GetTypedAccount() if err != nil { return err } @@ -108,24 +119,13 @@ func (v *TimedUserValidator) Add(user *protocol.User) error { nowSec := time.Now().Unix() - entry := &idEntry{ - 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) - for _, alterid := range account.AlterIDs { - entry := &idEntry{ - 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) + uu := &user{ + user: u, + account: account, + lastSec: protocol.Timestamp(nowSec - cacheDurationSec), } + v.users = append(v.users, uu) + v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), uu) return nil } @@ -138,7 +138,36 @@ 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 00000000..b3c80559 --- /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) +} diff --git a/release/release-ci.sh b/release/release-ci.sh index ecb95333..51b75221 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 @@ -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() { diff --git a/router.go b/router.go new file mode 100644 index 00000000..d3a8031f --- /dev/null +++ b/router.go @@ -0,0 +1,115 @@ +package core + +import ( + "context" + "sync" + + "v2ray.com/core/common" + "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() error { + d.RLock() + defer d.RUnlock() + + return common.Close(d.Dispatcher) +} + +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() error { + r.RLock() + defer r.RUnlock() + + return common.Close(r.Router) +} + +func (r *syncRouter) Set(router Router) { + r.Lock() + defer r.Unlock() + + r.Router = router +} diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go new file mode 100644 index 00000000..27165a36 --- /dev/null +++ b/testing/scenarios/command_test.go @@ -0,0 +1,130 @@ +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", + Service: []*serial.TypedMessage{ + serial.ToTypedMessage(&command.Config{}), + }, + }), + serial.ToTypedMessage(&router.Config{ + Rule: []*router.RoutingRule{ + { + InboundTag: []string{"api"}, + Tag: "api", + }, + }, + }), + }, + 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) +} diff --git a/testing/scenarios/common.go b/testing/scenarios/common.go index 6d054233..ac6155bb 100644 --- a/testing/scenarios/common.go +++ b/testing/scenarios/common.go @@ -13,14 +13,17 @@ 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 { - listener, err := net.Listen("tcp4", ":0") + listener, err := net.Listen("tcp4", "127.0.0.1:0") common.Must(err) defer listener.Close() @@ -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 66527077..5f168211 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 11058491..2a821f35 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 6c151314..22c3ff72 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 0e4752ae..942718e3 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 931b6402..7f827bb9 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 5351ad1c..04ebe966 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 76987d7a..c6d5505d 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 a657366e..253cd280 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 9b494fb9..c392d91f 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/testing/servers/http/http.go b/testing/servers/http/http.go index 230e973c..6bada232 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 48c819c0..a54e4b39 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 ff4c789d..22e47689 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/config.pb.go b/transport/internet/config.pb.go index 80e4e5c2..ff89b9c7 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/kcp/connection.go b/transport/internet/kcp/connection.go index d7e69f9d..fd039605 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 } @@ -338,24 +340,39 @@ 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 { - if c == nil || c.State() != StateActive { - return totalWritten, io.ErrClosedPipe - } + totalWritten := 0 + 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 + } + + updatePending = 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 updatePending { + c.dataUpdater.WakeUp() + updatePending = false + } + if err := c.waitForDataOutput(); err != nil { return totalWritten, err } @@ -366,20 +383,35 @@ func (c *Connection) Write(b []byte) (int, error) { func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error { defer mb.Release() - 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]) - }) { + updatePending := false + defer func() { + if updatePending { c.dataUpdater.WakeUp() + } + }() + + for { + for { + if c == nil || c.State() != StateActive { + return io.ErrClosedPipe + } + + if !c.sendingWorker.Push(func(bb []byte) (int, error) { + return mb.Read(bb[:c.mss]) + }) { + break + } + updatePending = true if mb.IsEmpty() { return nil } } + if updatePending { + c.dataUpdater.WakeUp() + updatePending = false + } + if err := c.waitForDataOutput(); err != nil { return err } @@ -555,7 +587,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() @@ -611,7 +643,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/kcp_test.go b/transport/internet/kcp/kcp_test.go index d7625b3b..7487cf1a 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) diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index 993f2fce..3fdcbf9f 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, } @@ -74,25 +72,19 @@ 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(): - return - default: - } - - if v.hub == nil { + if l.hub == nil { return } @@ -104,7 +96,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 +104,66 @@ 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) { - return - } - v.sessions[id] = conn + l.addConn(netConn) + l.sessions[id] = conn } conn.Input(segments) } -func (v *Listener) Remove(id ConnectionID) { - select { - case <-v.ctx.Done(): - return - default: - v.Lock() - delete(v.sessions, id) - v.Unlock() - } +func (l *Listener) Remove(id ConnectionID) { + 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,16 +173,16 @@ 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 } -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/kcp/receiving.go b/transport/internet/kcp/receiving.go index 65e3afc5..5789ae07 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 7885c472..5acf25be 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 57790f16..f12d488e 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 7b2d9769..0bc02c8d 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 { diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 4047c71a..05c7e4eb 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 5f1a1d29..081b04aa 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.go b/transport/internet/udp/dispatcher.go index 72cd3071..0db6ae54 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/transport/internet/udp/dispatcher_test.go b/transport/internet/udp/dispatcher_test.go index 76a939e7..e6c1958a 100644 --- a/transport/internet/udp/dispatcher_test.go +++ b/transport/internet/udp/dispatcher_test.go @@ -21,6 +21,14 @@ 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() 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 d6b15178..5fb26759 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" @@ -60,10 +58,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 { @@ -74,7 +73,6 @@ type ListenOption struct { type Hub struct { conn *net.UDPConn - cancel context.CancelFunc queue *PayloadQueue option ListenOption } @@ -105,20 +103,18 @@ 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() { - h.cancel() +func (h *Hub) Close() error { h.conn.Close() + return nil } func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) { @@ -128,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 @@ -151,7 +141,7 @@ L: if err != nil { newError("failed to read UDP msg").Base(err).WriteToLog() buffer.Release() - continue + break } payload := Payload{ diff --git a/transport/internet/websocket/hub.go b/transport/internet/websocket/hub.go index a5ec0eb0..36275fe2 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/internet/websocket/ws_test.go b/transport/internet/websocket/ws_test.go index 4f3c906c..1cb985b6 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() diff --git a/transport/ray/connection.go b/transport/ray/connection.go new file mode 100644 index 00000000..2bc4d39b --- /dev/null +++ b/transport/ray/connection.go @@ -0,0 +1,129 @@ +package ray + +import ( + "io" + "net" + "time" + + "v2ray.com/core/common/buf" + "v2ray.com/core/common/signal" +) + +type ConnectionOption func(*connection) + +func ConnLocalAddr(addr net.Addr) ConnectionOption { + return func(c *connection) { + c.localAddr = addr + } +} + +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 { + 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.output.WriteMultiBuffer(mb) +} + +func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { + if c.closed { + return io.ErrClosedPipe + } + + return c.output.WriteMultiBuffer(mb) +} + +// Close implements net.Conn.Close(). +func (c *connection) Close() error { + c.closed = true + c.output.Close() + c.input.CloseError() + if c.closeSignal != nil { + c.closeSignal.Signal() + } + 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 +} diff --git a/transport/ray/direct.go b/transport/ray/direct.go index efcb71cd..53af0f4d 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 46d41f7b..557bc64e 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 f6fcb49e..a95a14b5 100644 --- a/v2ray.go +++ b/v2ray.go @@ -2,140 +2,203 @@ package core import ( "context" + "sync" - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" - "v2ray.com/core/app/policy" - "v2ray.com/core/app/proxyman" "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. +// 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 + clock syncClock + cmd syncCommander + + access sync.Mutex + features []Feature + id uuid.UUID + running bool } -// 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 = &Instance{ + id: uuid.New(), + } if err := config.Transport.Apply(); err != nil { return nil, err } - space := app.NewSpace() - ctx := app.ContextWithSpace(context.Background(), space) - - server.space = space - for _, appSettings := range config.App { settings, err := appSettings.GetInstance() if err != nil { return nil, err } - application, err := app.CreateAppFromConfig(ctx, settings) - if err != nil { + if _, err := server.CreateObject(settings); err != nil { return nil, err } - if err := space.AddApplication(application); err != nil { - return nil, err - } - } - - 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)) } for _, inbound := range config.Inbound { - if err := inboundHandlerManager.AddHandler(ctx, inbound); err != nil { + rawHandler, err := server.CreateObject(inbound) + if err != nil { + return nil, err + } + handler, ok := rawHandler.(InboundHandler) + if !ok { + return nil, newError("not an InboundHandler") + } + if err := server.InboundHandlerManager().AddHandler(context.Background(), handler); err != nil { return nil, err } } for _, outbound := range config.Outbound { - if err := outboundHandlerManager.AddHandler(ctx, outbound); err != nil { + rawHandler, err := server.CreateObject(outbound) + if err != nil { + return nil, err + } + handler, ok := rawHandler.(OutboundHandler) + if !ok { + return nil, newError("not an OutboundHandler") + } + if err := server.OutboundHandlerManager().AddHandler(context.Background(), 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() +func (s *Instance) CreateObject(config interface{}) (interface{}, error) { + ctx := context.WithValue(context.Background(), v2rayKey, s) + return common.CreateObject(ctx, config) } -func (s *simpleServer) Start() error { - if err := s.space.Start(); err != nil { - return err +// 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() error { + s.access.Lock() + defer s.access.Unlock() + + s.running = false + 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. +// 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() + + s.running = true + for _, f := range s.features { + if err := f.Start(); err != nil { + return err + } + } + 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)) + case Clock, *Clock: + s.clock.Set(instance.(Clock)) + 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 +} + +// 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) +} + +// Clock returns the Clock used by this Instance. The returned Clock is always functional. +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) +} diff --git a/v2ray_test.go b/v2ray_test.go index 4374340c..7f287a50 100644 --- a/v2ray_test.go +++ b/v2ray_test.go @@ -21,8 +21,13 @@ func TestV2RayClose(t *testing.T) { assert := With(t) port := net.Port(dice.RollUint16()) + userId := uuid.New() config := &Config{ - Inbound: []*proxyman.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 +42,7 @@ func TestV2RayClose(t *testing.T) { }), }, }, - Outbound: []*proxyman.OutboundHandlerConfig{ + Outbound: []*OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ @@ -47,7 +52,7 @@ func TestV2RayClose(t *testing.T) { User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ - Id: uuid.New().String(), + Id: userId.String(), }), }, },