diff --git a/app/dns/server_test.go b/app/dns/server_test.go index a99a12f83..83e40cc64 100644 --- a/app/dns/server_test.go +++ b/app/dns/server_test.go @@ -20,7 +20,7 @@ func TestDnsAdd(t *testing.T) { space := app.NewSpace() outboundHandlerManager := proxyman.NewDefaultOutboundHandlerManager() - outboundHandlerManager.SetDefaultHandler(freedom.NewFreedomConnection(&freedom.Config{}, space)) + outboundHandlerManager.SetDefaultHandler(freedom.NewFreedomConnection(&freedom.Config{}, space, v2net.AnyIP)) space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager) space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space)) diff --git a/common/net/network.go b/common/net/network.go index 8ad935fd4..218f3daf5 100644 --- a/common/net/network.go +++ b/common/net/network.go @@ -22,6 +22,10 @@ func (this Network) AsList() *NetworkList { return &list } +func (this Network) String() string { + return string(this) +} + // NetworkList is a list of Networks. type NetworkList []Network diff --git a/common/net/testing/port.go b/common/net/testing/port.go index 465f86dbd..d368677f7 100644 --- a/common/net/testing/port.go +++ b/common/net/testing/port.go @@ -6,5 +6,5 @@ import ( ) func PickPort() v2net.Port { - return v2net.Port(30000 + dice.Roll(5000)) + return v2net.Port(30000 + dice.Roll(20000)) } diff --git a/common/protocol/user_validator.go b/common/protocol/user_validator.go index 0e3f68e25..59b871a4b 100644 --- a/common/protocol/user_validator.go +++ b/common/protocol/user_validator.go @@ -50,7 +50,7 @@ func NewTimedUserValidator(hasher IDHash) UserValidator { hasher: hasher, cancel: signal.NewCloseSignal(), } - go tus.updateUserHash(time.Tick(updateIntervalSec*time.Second), tus.cancel) + go tus.updateUserHash(updateIntervalSec*time.Second, tus.cancel) return tus } @@ -88,11 +88,11 @@ func (this *TimedUserValidator) generateNewHashes(nowSec Timestamp, idx int, ent } } -func (this *TimedUserValidator) updateUserHash(tick <-chan time.Time, cancel *signal.CancelSignal) { +func (this *TimedUserValidator) updateUserHash(interval time.Duration, cancel *signal.CancelSignal) { L: for { select { - case now := <-tick: + case now := <-time.After(interval): nowSec := Timestamp(now.Unix() + cacheDurationSec) for _, entry := range this.ids { this.generateNewHashes(nowSec, entry.userIdx, entry) diff --git a/proxy/blackhole/blackhole.go b/proxy/blackhole/blackhole.go index 95afd5b54..7d57a7a63 100644 --- a/proxy/blackhole/blackhole.go +++ b/proxy/blackhole/blackhole.go @@ -31,7 +31,7 @@ func (this *BlackHole) Dispatch(destination v2net.Destination, payload *alloc.Bu func init() { internal.MustRegisterOutboundHandlerCreator("blackhole", - func(space app.Space, config interface{}) (proxy.OutboundHandler, error) { + func(space app.Space, config interface{}, sendThrough v2net.Address) (proxy.OutboundHandler, error) { return NewBlackHole(), nil }) } diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 067cd21cf..81376855f 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -29,11 +29,13 @@ type DokodemoDoor struct { listeningAddress v2net.Address } -func NewDokodemoDoor(config *Config, space app.Space) *DokodemoDoor { +func NewDokodemoDoor(config *Config, space app.Space, listen v2net.Address, port v2net.Port) *DokodemoDoor { d := &DokodemoDoor{ - config: config, - address: config.Address, - port: config.Port, + config: config, + address: config.Address, + port: config.Port, + listeningAddress: listen, + listeningPort: port, } space.InitializeApplication(func() error { if !space.HasApp(dispatcher.APP_ID) { @@ -66,26 +68,20 @@ func (this *DokodemoDoor) Close() { } } -func (this *DokodemoDoor) Listen(address v2net.Address, port v2net.Port) error { +func (this *DokodemoDoor) Start() error { if this.accepting { - if this.listeningPort == port && this.listeningAddress.Equals(address) { - return nil - } else { - return proxy.ErrorAlreadyListening - } + return nil } - this.listeningPort = port - this.listeningAddress = address this.accepting = true if this.config.Network.HasNetwork(v2net.TCPNetwork) { - err := this.ListenTCP(address, port) + err := this.ListenTCP(this.listeningAddress, this.listeningPort) if err != nil { return err } } if this.config.Network.HasNetwork(v2net.UDPNetwork) { - err := this.ListenUDP(address, port) + err := this.ListenUDP(this.listeningAddress, this.listeningPort) if err != nil { return err } @@ -168,7 +164,7 @@ func (this *DokodemoDoor) HandleTCPConnection(conn *hub.Connection) { func init() { internal.MustRegisterInboundHandlerCreator("dokodemo-door", - func(space app.Space, rawConfig interface{}) (proxy.InboundHandler, error) { - return NewDokodemoDoor(rawConfig.(*Config), space), nil + func(space app.Space, rawConfig interface{}, listen v2net.Address, port v2net.Port) (proxy.InboundHandler, error) { + return NewDokodemoDoor(rawConfig.(*Config), space, listen, port), nil }) } diff --git a/proxy/dokodemo/dokodemo_test.go b/proxy/dokodemo/dokodemo_test.go index 96c4cd25e..45f4f04b8 100644 --- a/proxy/dokodemo/dokodemo_test.go +++ b/proxy/dokodemo/dokodemo_test.go @@ -37,23 +37,23 @@ func TestDokodemoTCP(t *testing.T) { space := app.NewSpace() space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space)) ohm := proxyman.NewDefaultOutboundHandlerManager() - ohm.SetDefaultHandler(freedom.NewFreedomConnection(&freedom.Config{}, space)) + ohm.SetDefaultHandler(freedom.NewFreedomConnection(&freedom.Config{}, space, v2net.LocalHostIP)) space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, ohm) data2Send := "Data to be sent to remote." + port := v2nettesting.PickPort() dokodemo := NewDokodemoDoor(&Config{ Address: v2net.LocalHostIP, Port: tcpServer.Port, Network: v2net.TCPNetwork.AsList(), Timeout: 600, - }, space) + }, space, v2net.LocalHostIP, port) defer dokodemo.Close() assert.Error(space.Initialize()).IsNil() - port := v2nettesting.PickPort() - err = dokodemo.Listen(v2net.LocalHostIP, port) + err = dokodemo.Start() assert.Error(err).IsNil() assert.Port(port).Equals(dokodemo.Port()) @@ -95,23 +95,23 @@ func TestDokodemoUDP(t *testing.T) { space := app.NewSpace() space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space)) ohm := proxyman.NewDefaultOutboundHandlerManager() - ohm.SetDefaultHandler(freedom.NewFreedomConnection(&freedom.Config{}, space)) + ohm.SetDefaultHandler(freedom.NewFreedomConnection(&freedom.Config{}, space, v2net.AnyIP)) space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, ohm) data2Send := "Data to be sent to remote." + port := v2nettesting.PickPort() dokodemo := NewDokodemoDoor(&Config{ Address: v2net.LocalHostIP, Port: udpServer.Port, Network: v2net.UDPNetwork.AsList(), Timeout: 600, - }, space) + }, space, v2net.LocalHostIP, port) defer dokodemo.Close() assert.Error(space.Initialize()).IsNil() - port := v2nettesting.PickPort() - err = dokodemo.Listen(v2net.LocalHostIP, port) + err = dokodemo.Start() assert.Error(err).IsNil() assert.Port(port).Equals(dokodemo.Port()) diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index d4cf38ac9..f451de99f 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -23,12 +23,14 @@ type FreedomConnection struct { domainStrategy DomainStrategy timeout uint32 dns dns.Server + sendThrough v2net.Address } -func NewFreedomConnection(config *Config, space app.Space) *FreedomConnection { +func NewFreedomConnection(config *Config, space app.Space, sendThrough v2net.Address) *FreedomConnection { f := &FreedomConnection{ domainStrategy: config.DomainStrategy, timeout: config.Timeout, + sendThrough: sendThrough, } space.InitializeApplication(func() error { if config.DomainStrategy == DomainStrategyUseIP { @@ -78,7 +80,7 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload * destination = this.ResolveIP(destination) } err := retry.Timed(5, 100).On(func() error { - rawConn, err := hub.DialWithoutCache(destination) + rawConn, err := hub.DialWithoutCache(this.sendThrough, destination) if err != nil { return err } @@ -138,7 +140,7 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload * func init() { internal.MustRegisterOutboundHandlerCreator("freedom", - func(space app.Space, config interface{}) (proxy.OutboundHandler, error) { - return NewFreedomConnection(config.(*Config), space), nil + func(space app.Space, config interface{}, sendThrough v2net.Address) (proxy.OutboundHandler, error) { + return NewFreedomConnection(config.(*Config), space, sendThrough), nil }) } diff --git a/proxy/freedom/freedom_test.go b/proxy/freedom/freedom_test.go index f4aa5a66f..8bda6a835 100644 --- a/proxy/freedom/freedom_test.go +++ b/proxy/freedom/freedom_test.go @@ -37,7 +37,7 @@ func TestSinglePacket(t *testing.T) { assert.Error(err).IsNil() space := app.NewSpace() - freedom := NewFreedomConnection(&Config{}, space) + freedom := NewFreedomConnection(&Config{}, space, v2net.AnyIP) space.Initialize() traffic := ray.NewRay() @@ -57,7 +57,7 @@ func TestSinglePacket(t *testing.T) { func TestUnreachableDestination(t *testing.T) { assert := assert.On(t) - freedom := NewFreedomConnection(&Config{}, app.NewSpace()) + freedom := NewFreedomConnection(&Config{}, app.NewSpace(), v2net.AnyIP) traffic := ray.NewRay() data2Send := "Data to be sent to remote" payload := alloc.NewSmallBuffer().Clear().Append([]byte(data2Send)) @@ -81,7 +81,7 @@ func TestIPResolution(t *testing.T) { }) space.BindApp(dns.APP_ID, dnsServer) - freedom := NewFreedomConnection(&Config{DomainStrategy: DomainStrategyUseIP}, space) + freedom := NewFreedomConnection(&Config{DomainStrategy: DomainStrategyUseIP}, space, v2net.AnyIP) space.Initialize() diff --git a/proxy/http/server.go b/proxy/http/server.go index 965b486c7..18c2ac632 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -33,10 +33,12 @@ type Server struct { listeningAddress v2net.Address } -func NewServer(config *Config, packetDispatcher dispatcher.PacketDispatcher) *Server { +func NewServer(config *Config, packetDispatcher dispatcher.PacketDispatcher, listen v2net.Address, port v2net.Port) *Server { return &Server{ packetDispatcher: packetDispatcher, config: config, + listeningAddress: listen, + listeningPort: port, } } @@ -54,24 +56,18 @@ func (this *Server) Close() { } } -func (this *Server) Listen(address v2net.Address, port v2net.Port) error { +func (this *Server) Start() error { if this.accepting { - if this.listeningPort == port && this.listeningAddress.Equals(address) { - return nil - } else { - return proxy.ErrorAlreadyListening - } + return nil } - this.listeningPort = port - this.listeningAddress = address var tlsConfig *tls.Config if this.config.TLSConfig != nil { tlsConfig = this.config.TLSConfig.GetConfig() } - tcpListener, err := hub.ListenTCP(address, port, this.handleConnection, tlsConfig) + tcpListener, err := hub.ListenTCP(this.listeningAddress, this.listeningPort, this.handleConnection, tlsConfig) if err != nil { - log.Error("HTTP: Failed listen on port ", port, ": ", err) + log.Error("HTTP: Failed listen on port ", this.listeningPort, ": ", err) return err } this.Lock() @@ -276,12 +272,14 @@ func (this *Server) handlePlainHTTP(request *http.Request, dest v2net.Destinatio func init() { internal.MustRegisterInboundHandlerCreator("http", - func(space app.Space, rawConfig interface{}) (proxy.InboundHandler, error) { + func(space app.Space, rawConfig interface{}, listen v2net.Address, port v2net.Port) (proxy.InboundHandler, error) { if !space.HasApp(dispatcher.APP_ID) { return nil, internal.ErrorBadConfiguration } return NewServer( rawConfig.(*Config), - space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher)), nil + space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher), + listen, + port), nil }) } diff --git a/proxy/http/server_test.go b/proxy/http/server_test.go index b4b1b880d..ef5eec7f8 100644 --- a/proxy/http/server_test.go +++ b/proxy/http/server_test.go @@ -52,11 +52,11 @@ func TestNormalGetRequest(t *testing.T) { testPacketDispatcher := testdispatcher.NewTestPacketDispatcher(nil) - httpProxy := NewServer(&Config{}, testPacketDispatcher) + port := v2nettesting.PickPort() + httpProxy := NewServer(&Config{}, testPacketDispatcher, v2net.LocalHostIP, port) defer httpProxy.Close() - port := v2nettesting.PickPort() - err := httpProxy.Listen(v2net.LocalHostIP, port) + err := httpProxy.Start() assert.Error(err).IsNil() assert.Port(port).Equals(httpProxy.Port()) diff --git a/proxy/internal/creator.go b/proxy/internal/creator.go index 11f1bcf89..5a20562fe 100644 --- a/proxy/internal/creator.go +++ b/proxy/internal/creator.go @@ -2,8 +2,9 @@ package internal import ( "github.com/v2ray/v2ray-core/app" + v2net "github.com/v2ray/v2ray-core/common/net" "github.com/v2ray/v2ray-core/proxy" ) -type InboundHandlerCreator func(space app.Space, config interface{}) (proxy.InboundHandler, error) -type OutboundHandlerCreator func(space app.Space, config interface{}) (proxy.OutboundHandler, error) +type InboundHandlerCreator func(space app.Space, config interface{}, listenOn v2net.Address, port v2net.Port) (proxy.InboundHandler, error) +type OutboundHandlerCreator func(space app.Space, config interface{}, sendThrough v2net.Address) (proxy.OutboundHandler, error) diff --git a/proxy/internal/handler_cache.go b/proxy/internal/handler_cache.go index d9755eecc..de8eded17 100644 --- a/proxy/internal/handler_cache.go +++ b/proxy/internal/handler_cache.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/v2ray/v2ray-core/app" + v2net "github.com/v2ray/v2ray-core/common/net" "github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/proxy/internal/config" ) @@ -45,7 +46,7 @@ func MustRegisterOutboundHandlerCreator(name string, creator OutboundHandlerCrea } } -func CreateInboundHandler(name string, space app.Space, rawConfig []byte) (proxy.InboundHandler, error) { +func CreateInboundHandler(name string, space app.Space, rawConfig []byte, listen v2net.Address, port v2net.Port) (proxy.InboundHandler, error) { creator, found := inboundFactories[name] if !found { return nil, ErrorProxyNotFound @@ -55,12 +56,12 @@ func CreateInboundHandler(name string, space app.Space, rawConfig []byte) (proxy if err != nil { return nil, err } - return creator(space, proxyConfig) + return creator(space, proxyConfig, listen, port) } - return creator(space, nil) + return creator(space, nil, listen, port) } -func CreateOutboundHandler(name string, space app.Space, rawConfig []byte) (proxy.OutboundHandler, error) { +func CreateOutboundHandler(name string, space app.Space, rawConfig []byte, sendThrough v2net.Address) (proxy.OutboundHandler, error) { creator, found := outboundFactories[name] if !found { return nil, ErrorNameExists @@ -71,8 +72,8 @@ func CreateOutboundHandler(name string, space app.Space, rawConfig []byte) (prox if err != nil { return nil, err } - return creator(space, proxyConfig) + return creator(space, proxyConfig, sendThrough) } - return creator(space, nil) + return creator(space, nil, sendThrough) } diff --git a/proxy/proxy.go b/proxy/proxy.go index 862e0edbd..947adb6af 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -16,8 +16,8 @@ const ( // An InboundHandler handles inbound network connections to V2Ray. type InboundHandler interface { - // Listen starts a InboundHandler by listen on a specific port. - Listen(on v2net.Address, port v2net.Port) error + // Listen starts a InboundHandler. + Start() error // Close stops the handler to accepting anymore inbound connections. Close() // Port returns the port that the handler is listening on. diff --git a/proxy/repo/repo.go b/proxy/repo/repo.go index bbf6b01b1..baf07a859 100644 --- a/proxy/repo/repo.go +++ b/proxy/repo/repo.go @@ -2,14 +2,15 @@ package repo import ( "github.com/v2ray/v2ray-core/app" + v2net "github.com/v2ray/v2ray-core/common/net" "github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/proxy/internal" ) -func CreateInboundHandler(name string, space app.Space, rawConfig []byte) (proxy.InboundHandler, error) { - return internal.CreateInboundHandler(name, space, rawConfig) +func CreateInboundHandler(name string, space app.Space, rawConfig []byte, listen v2net.Address, port v2net.Port) (proxy.InboundHandler, error) { + return internal.CreateInboundHandler(name, space, rawConfig, listen, port) } -func CreateOutboundHandler(name string, space app.Space, rawConfig []byte) (proxy.OutboundHandler, error) { - return internal.CreateOutboundHandler(name, space, rawConfig) +func CreateOutboundHandler(name string, space app.Space, rawConfig []byte, sendThrough v2net.Address) (proxy.OutboundHandler, error) { + return internal.CreateOutboundHandler(name, space, rawConfig, sendThrough) } diff --git a/proxy/shadowsocks/protocol_test.go b/proxy/shadowsocks/protocol_test.go index 0c0e3c10f..90e2228e6 100644 --- a/proxy/shadowsocks/protocol_test.go +++ b/proxy/shadowsocks/protocol_test.go @@ -1,6 +1,7 @@ package shadowsocks_test import ( + "io" "testing" "github.com/v2ray/v2ray-core/common/alloc" @@ -29,7 +30,7 @@ func TestEmptyPayload(t *testing.T) { buffer := alloc.NewSmallBuffer().Clear() _, err := ReadRequest(buffer, nil, false) - assert.Error(err).Equals(transport.ErrorCorruptedPacket) + assert.Error(err).Equals(io.EOF) } func TestSingleBytePayload(t *testing.T) { diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 6774fa986..85caff7f4 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -30,10 +30,12 @@ type Server struct { udpServer *hub.UDPServer } -func NewServer(config *Config, packetDispatcher dispatcher.PacketDispatcher) *Server { +func NewServer(config *Config, packetDispatcher dispatcher.PacketDispatcher, listen v2net.Address, port v2net.Port) *Server { return &Server{ config: config, packetDispatcher: packetDispatcher, + address: listen, + port: port, } } @@ -56,34 +58,28 @@ func (this *Server) Close() { } -func (this *Server) Listen(address v2net.Address, port v2net.Port) error { +func (this *Server) Start() error { if this.accepting { - if this.port == port && this.address.Equals(address) { - return nil - } else { - return proxy.ErrorAlreadyListening - } + return nil } - tcpHub, err := hub.ListenTCP(address, port, this.handleConnection, nil) + tcpHub, err := hub.ListenTCP(this.address, this.port, this.handleConnection, nil) if err != nil { - log.Error("Shadowsocks: Failed to listen TCP on port ", port, ": ", err) + log.Error("Shadowsocks: Failed to listen TCP on port ", this.port, ": ", err) return err } this.tcpHub = tcpHub if this.config.UDP { this.udpServer = hub.NewUDPServer(this.packetDispatcher) - udpHub, err := hub.ListenUDP(address, port, this.handlerUDPPayload) + udpHub, err := hub.ListenUDP(this.address, this.port, this.handlerUDPPayload) if err != nil { - log.Error("Shadowsocks: Failed to listen UDP on port ", port, ": ", err) + log.Error("Shadowsocks: Failed to listen UDP on port ", this.port, ": ", err) return err } this.udpHub = udpHub } - this.port = port - this.address = address this.accepting = true return nil @@ -256,12 +252,14 @@ func (this *Server) handleConnection(conn *hub.Connection) { func init() { internal.MustRegisterInboundHandlerCreator("shadowsocks", - func(space app.Space, rawConfig interface{}) (proxy.InboundHandler, error) { + func(space app.Space, rawConfig interface{}, listen v2net.Address, port v2net.Port) (proxy.InboundHandler, error) { if !space.HasApp(dispatcher.APP_ID) { return nil, internal.ErrorBadConfiguration } return NewServer( rawConfig.(*Config), - space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher)), nil + space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher), + listen, + port), nil }) } diff --git a/proxy/socks/server.go b/proxy/socks/server.go index dfde068f1..09242c7c0 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -38,10 +38,12 @@ type Server struct { } // NewServer creates a new Server object. -func NewServer(config *Config, packetDispatcher dispatcher.PacketDispatcher) *Server { +func NewServer(config *Config, packetDispatcher dispatcher.PacketDispatcher, listen v2net.Address, port v2net.Port) *Server { return &Server{ config: config, packetDispatcher: packetDispatcher, + listeningAddress: listen, + listeningPort: port, } } @@ -68,20 +70,18 @@ func (this *Server) Close() { } // Listen implements InboundHandler.Listen(). -func (this *Server) Listen(address v2net.Address, port v2net.Port) error { +func (this *Server) Start() error { if this.accepting { - if this.listeningPort == port && this.listeningAddress.Equals(address) { - return nil - } else { - return proxy.ErrorAlreadyListening - } + return nil } - this.listeningPort = port - this.listeningAddress = address - listener, err := hub.ListenTCP(address, port, this.handleConnection, nil) + listener, err := hub.ListenTCP( + this.listeningAddress, + this.listeningPort, + this.handleConnection, + nil) if err != nil { - log.Error("Socks: failed to listen on port ", port, ": ", err) + log.Error("Socks: failed to listen on port ", this.listeningPort, ": ", err) return err } this.accepting = true @@ -89,7 +89,7 @@ func (this *Server) Listen(address v2net.Address, port v2net.Port) error { this.tcpListener = listener this.tcpMutex.Unlock() if this.config.UDPEnabled { - this.listenUDP(address, port) + this.listenUDP(this.listeningAddress, this.listeningPort) } return nil } @@ -301,12 +301,14 @@ func (this *Server) transport(reader io.Reader, writer io.Writer, destination v2 func init() { internal.MustRegisterInboundHandlerCreator("socks", - func(space app.Space, rawConfig interface{}) (proxy.InboundHandler, error) { + func(space app.Space, rawConfig interface{}, listen v2net.Address, port v2net.Port) (proxy.InboundHandler, error) { if !space.HasApp(dispatcher.APP_ID) { return nil, internal.ErrorBadConfiguration } return NewServer( rawConfig.(*Config), - space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher)), nil + space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher), + listen, + port), nil }) } diff --git a/proxy/socks/server_test.go b/proxy/socks/server_test.go index 4fa6b695c..cc37b27ed 100644 --- a/proxy/socks/server_test.go +++ b/proxy/socks/server_test.go @@ -31,16 +31,17 @@ func TestSocksTcpConnect(t *testing.T) { ConnInput: bytes.NewReader(connInput), } - protocol, err := proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", func(space app.Space, config interface{}) (v2proxy.OutboundHandler, error) { - return och, nil - }) + protocol, err := proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", + func(space app.Space, config interface{}, sendThrough v2net.Address) (v2proxy.OutboundHandler, error) { + return och, nil + }) assert.Error(err).IsNil() config := &point.Config{ - Port: port, - ListenOn: v2net.LocalHostIP, - InboundConfig: &point.ConnectionConfig{ + Port: port, + InboundConfig: &point.InboundConnectionConfig{ Protocol: "socks", + ListenOn: v2net.LocalHostIP, Settings: []byte(` { "auth": "noauth" @@ -51,7 +52,7 @@ func TestSocksTcpConnect(t *testing.T) { v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), }, }, - OutboundConfig: &point.ConnectionConfig{ + OutboundConfig: &point.OutboundConnectionConfig{ Protocol: protocol, Settings: nil, }, @@ -96,16 +97,17 @@ func TestSocksTcpConnectWithUserPass(t *testing.T) { ConnOutput: connOutput, } - protocol, err := proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", func(space app.Space, config interface{}) (v2proxy.OutboundHandler, error) { - return och, nil - }) + protocol, err := proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", + func(space app.Space, config interface{}, sendThrough v2net.Address) (v2proxy.OutboundHandler, error) { + return och, nil + }) assert.Error(err).IsNil() config := &point.Config{ - Port: port, - ListenOn: v2net.LocalHostIP, - InboundConfig: &point.ConnectionConfig{ + Port: port, + InboundConfig: &point.InboundConnectionConfig{ Protocol: "socks", + ListenOn: v2net.LocalHostIP, Settings: []byte(` { "auth": "password", @@ -119,7 +121,7 @@ func TestSocksTcpConnectWithUserPass(t *testing.T) { v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), }, }, - OutboundConfig: &point.ConnectionConfig{ + OutboundConfig: &point.OutboundConnectionConfig{ Protocol: protocol, Settings: nil, }, @@ -164,16 +166,17 @@ func TestSocksTcpConnectWithWrongUserPass(t *testing.T) { ConnOutput: connOutput, } - protocol, err := proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", func(space app.Space, config interface{}) (v2proxy.OutboundHandler, error) { - return och, nil - }) + protocol, err := proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", + func(space app.Space, config interface{}, sendThrough v2net.Address) (v2proxy.OutboundHandler, error) { + return och, nil + }) assert.Error(err).IsNil() config := &point.Config{ - Port: port, - ListenOn: v2net.LocalHostIP, - InboundConfig: &point.ConnectionConfig{ + Port: port, + InboundConfig: &point.InboundConnectionConfig{ Protocol: "socks", + ListenOn: v2net.LocalHostIP, Settings: []byte(` { "auth": "password", @@ -187,7 +190,7 @@ func TestSocksTcpConnectWithWrongUserPass(t *testing.T) { v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), }, }, - OutboundConfig: &point.ConnectionConfig{ + OutboundConfig: &point.OutboundConnectionConfig{ Protocol: protocol, Settings: nil, }, @@ -218,15 +221,16 @@ func TestSocksTcpConnectWithWrongAuthMethod(t *testing.T) { ConnOutput: connOutput, } - protocol, err := proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", func(space app.Space, config interface{}) (v2proxy.OutboundHandler, error) { - return och, nil - }) + protocol, err := proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", + func(space app.Space, config interface{}, sendThrough v2net.Address) (v2proxy.OutboundHandler, error) { + return och, nil + }) assert.Error(err).IsNil() config := &point.Config{ - Port: port, - ListenOn: v2net.LocalHostIP, - InboundConfig: &point.ConnectionConfig{ + Port: port, + InboundConfig: &point.InboundConnectionConfig{ + ListenOn: v2net.LocalHostIP, Protocol: "socks", Settings: []byte(` { @@ -241,7 +245,7 @@ func TestSocksTcpConnectWithWrongAuthMethod(t *testing.T) { v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), }, }, - OutboundConfig: &point.ConnectionConfig{ + OutboundConfig: &point.OutboundConnectionConfig{ Protocol: protocol, Settings: nil, }, diff --git a/proxy/testing/mocks/inboundhandler.go b/proxy/testing/mocks/inboundhandler.go index 35bdfe900..2e076b7fe 100644 --- a/proxy/testing/mocks/inboundhandler.go +++ b/proxy/testing/mocks/inboundhandler.go @@ -10,21 +10,19 @@ import ( ) type InboundConnectionHandler struct { - port v2net.Port - address v2net.Address + ListeningPort v2net.Port + ListeningAddress v2net.Address PacketDispatcher dispatcher.PacketDispatcher ConnInput io.Reader ConnOutput io.Writer } -func (this *InboundConnectionHandler) Listen(address v2net.Address, port v2net.Port) error { - this.port = port - this.address = address +func (this *InboundConnectionHandler) Start() error { return nil } func (this *InboundConnectionHandler) Port() v2net.Port { - return this.port + return this.ListeningPort } func (this *InboundConnectionHandler) Close() { diff --git a/proxy/testing/mocks/outboundhandler.go b/proxy/testing/mocks/outboundhandler.go index 021684cff..b34a3a501 100644 --- a/proxy/testing/mocks/outboundhandler.go +++ b/proxy/testing/mocks/outboundhandler.go @@ -50,6 +50,6 @@ func (this *OutboundConnectionHandler) Dispatch(destination v2net.Destination, p return nil } -func (this *OutboundConnectionHandler) Create(space app.Space, config interface{}) (proxy.OutboundHandler, error) { +func (this *OutboundConnectionHandler) Create(space app.Space, config interface{}, sendThrough v2net.Address) (proxy.OutboundHandler, error) { return this, nil } diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 89c4ee5fe..002159fac 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -88,6 +88,8 @@ func (this *VMessInboundHandler) Close() { this.Lock() this.listener.Close() this.listener = nil + this.clients.Release() + this.clients = nil this.Unlock() } } @@ -100,20 +102,14 @@ func (this *VMessInboundHandler) GetUser(email string) *protocol.User { return user } -func (this *VMessInboundHandler) Listen(address v2net.Address, port v2net.Port) error { +func (this *VMessInboundHandler) Start() error { if this.accepting { - if this.listeningPort == port && this.listeningAddress.Equals(address) { - return nil - } else { - return proxy.ErrorAlreadyListening - } + return nil } - this.listeningPort = port - this.listeningAddress = address - tcpListener, err := hub.ListenTCP(address, port, this.HandleConnection, nil) + tcpListener, err := hub.ListenTCP(this.listeningAddress, this.listeningPort, this.HandleConnection, nil) if err != nil { - log.Error("Unable to listen tcp port ", port, ": ", err) + log.Error("Unable to listen tcp port ", this.listeningPort, ": ", err) return err } this.accepting = true @@ -224,7 +220,7 @@ func (this *VMessInboundHandler) HandleConnection(connection *hub.Connection) { func init() { internal.MustRegisterInboundHandlerCreator("vmess", - func(space app.Space, rawConfig interface{}) (proxy.InboundHandler, error) { + func(space app.Space, rawConfig interface{}, listen v2net.Address, port v2net.Port) (proxy.InboundHandler, error) { if !space.HasApp(dispatcher.APP_ID) { return nil, internal.ErrorBadConfiguration } @@ -240,6 +236,8 @@ func init() { clients: allowedClients, detours: config.DetourConfig, usersByEmail: NewUserByEmail(config.AllowedUsers, config.Defaults), + listeningAddress: listen, + listeningPort: port, } if space.HasApp(proxyman.APP_ID_INBOUND_MANAGER) { diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index 52ec05016..038385686 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -21,6 +21,7 @@ import ( type VMessOutboundHandler struct { receiverManager *ReceiverManager + sendThrough v2net.Address } func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error { @@ -42,7 +43,7 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al Option: protocol.RequestOptionChunkStream, } - conn, err := hub.Dial(destination) + conn, err := hub.Dial(this.sendThrough, destination) if err != nil { log.Error("Failed to open ", destination, ": ", err) return err @@ -142,10 +143,11 @@ func (this *VMessOutboundHandler) handleResponse(session *raw.ClientSession, con func init() { internal.MustRegisterOutboundHandlerCreator("vmess", - func(space app.Space, rawConfig interface{}) (proxy.OutboundHandler, error) { + func(space app.Space, rawConfig interface{}, sendThrough v2net.Address) (proxy.OutboundHandler, error) { vOutConfig := rawConfig.(*Config) return &VMessOutboundHandler{ receiverManager: NewReceiverManager(vOutConfig.Receivers), + sendThrough: sendThrough, }, nil }) } diff --git a/proxy/vmess/vmess_test.go b/proxy/vmess/vmess_test.go index e76b277f0..ea50e8890 100644 --- a/proxy/vmess/vmess_test.go +++ b/proxy/vmess/vmess_test.go @@ -38,25 +38,28 @@ func TestVMessInAndOut(t *testing.T) { ConnOutput: ichConnOutput, } - protocol, err := proxytesting.RegisterInboundConnectionHandlerCreator("mock_och", func(space app.Space, config interface{}) (proxy.InboundHandler, error) { - ich.PacketDispatcher = space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher) - return ich, nil - }) + protocol, err := proxytesting.RegisterInboundConnectionHandlerCreator("mock_ich", + func(space app.Space, config interface{}, listen v2net.Address, port v2net.Port) (proxy.InboundHandler, error) { + ich.ListeningAddress = listen + ich.ListeningPort = port + ich.PacketDispatcher = space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher) + return ich, nil + }) assert.Error(err).IsNil() configA := &point.Config{ - Port: portA, - ListenOn: v2net.LocalHostIP, + Port: portA, DNSConfig: &dns.Config{ NameServers: []v2net.Destination{ v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), }, }, - InboundConfig: &point.ConnectionConfig{ + InboundConfig: &point.InboundConnectionConfig{ Protocol: protocol, + ListenOn: v2net.LocalHostIP, Settings: nil, }, - OutboundConfig: &point.ConnectionConfig{ + OutboundConfig: &point.OutboundConnectionConfig{ Protocol: "vmess", Settings: []byte(`{ "vnext": [ @@ -85,28 +88,29 @@ func TestVMessInAndOut(t *testing.T) { ConnOutput: ochConnOutput, } - protocol, err = proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", func(space app.Space, config interface{}) (proxy.OutboundHandler, error) { - return och, nil - }) + protocol, err = proxytesting.RegisterOutboundConnectionHandlerCreator("mock_och", + func(space app.Space, config interface{}, sendThrough v2net.Address) (proxy.OutboundHandler, error) { + return och, nil + }) assert.Error(err).IsNil() configB := &point.Config{ - Port: portB, - ListenOn: v2net.LocalHostIP, + Port: portB, DNSConfig: &dns.Config{ NameServers: []v2net.Destination{ v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), }, }, - InboundConfig: &point.ConnectionConfig{ + InboundConfig: &point.InboundConnectionConfig{ Protocol: "vmess", + ListenOn: v2net.LocalHostIP, Settings: []byte(`{ "clients": [ {"id": "` + testAccount.String() + `"} ] }`), }, - OutboundConfig: &point.ConnectionConfig{ + OutboundConfig: &point.OutboundConnectionConfig{ Protocol: protocol, Settings: nil, }, diff --git a/release/config/vpoint_socks_vmess.json b/release/config/vpoint_socks_vmess.json index c0841df01..ef263e9e2 100644 --- a/release/config/vpoint_socks_vmess.json +++ b/release/config/vpoint_socks_vmess.json @@ -1,9 +1,10 @@ { - "port": 1080, "log": { - "access": "" + "loglevel": "warning" }, "inbound": { + "port": 1080, + "listen": "127.0.0.1", "protocol": "socks", "settings": { "auth": "noauth", diff --git a/release/config/vpoint_vmess_freedom.json b/release/config/vpoint_vmess_freedom.json index 76f179186..856b3d011 100644 --- a/release/config/vpoint_vmess_freedom.json +++ b/release/config/vpoint_vmess_freedom.json @@ -1,11 +1,11 @@ { - "port": 10086, "log" : { "access": "/var/log/v2ray/access.log", "error": "/var/log/v2ray/error.log", "loglevel": "warning" }, "inbound": { + "port": 10086, "protocol": "vmess", "settings": { "clients": [ diff --git a/shell/point/config.go b/shell/point/config.go index e17658c66..bf838bcc7 100644 --- a/shell/point/config.go +++ b/shell/point/config.go @@ -8,11 +8,19 @@ import ( "github.com/v2ray/v2ray-core/transport" ) -type ConnectionConfig struct { +type InboundConnectionConfig struct { + Port v2net.Port + ListenOn v2net.Address Protocol string Settings []byte } +type OutboundConnectionConfig struct { + Protocol string + SendThrough v2net.Address + Settings []byte +} + type LogConfig struct { AccessLog string ErrorLog string @@ -41,19 +49,19 @@ type InboundDetourConfig struct { } type OutboundDetourConfig struct { - Protocol string - Tag string - Settings []byte + Protocol string + SendThrough v2net.Address + Tag string + Settings []byte } type Config struct { Port v2net.Port - ListenOn v2net.Address LogConfig *LogConfig RouterConfig *router.Config DNSConfig *dns.Config - InboundConfig *ConnectionConfig - OutboundConfig *ConnectionConfig + InboundConfig *InboundConnectionConfig + OutboundConfig *OutboundConnectionConfig InboundDetours []*InboundDetourConfig OutboundDetours []*OutboundDetourConfig TransportConfig *transport.Config diff --git a/shell/point/config_json.go b/shell/point/config_json.go index c1c40d32d..32fbaf777 100644 --- a/shell/point/config_json.go +++ b/shell/point/config_json.go @@ -22,29 +22,21 @@ const ( func (this *Config) UnmarshalJSON(data []byte) error { type JsonConfig struct { - Port v2net.Port `json:"port"` // Port of this Point server. - ListenOn *v2net.AddressJson `json:"listen"` - LogConfig *LogConfig `json:"log"` - RouterConfig *router.Config `json:"routing"` - DNSConfig *dns.Config `json:"dns"` - InboundConfig *ConnectionConfig `json:"inbound"` - OutboundConfig *ConnectionConfig `json:"outbound"` - InboundDetours []*InboundDetourConfig `json:"inboundDetour"` - OutboundDetours []*OutboundDetourConfig `json:"outboundDetour"` - Transport *transport.Config `json:"transport"` + Port v2net.Port `json:"port"` // Port of this Point server. + LogConfig *LogConfig `json:"log"` + RouterConfig *router.Config `json:"routing"` + DNSConfig *dns.Config `json:"dns"` + InboundConfig *InboundConnectionConfig `json:"inbound"` + OutboundConfig *OutboundConnectionConfig `json:"outbound"` + InboundDetours []*InboundDetourConfig `json:"inboundDetour"` + OutboundDetours []*OutboundDetourConfig `json:"outboundDetour"` + Transport *transport.Config `json:"transport"` } jsonConfig := new(JsonConfig) if err := json.Unmarshal(data, jsonConfig); err != nil { return err } this.Port = jsonConfig.Port - this.ListenOn = v2net.AnyIP - if jsonConfig.ListenOn != nil { - if jsonConfig.ListenOn.Address.IsDomain() { - return errors.New("Point: Unable to listen on domain address: " + jsonConfig.ListenOn.Address.Domain()) - } - this.ListenOn = jsonConfig.ListenOn.Address - } this.LogConfig = jsonConfig.LogConfig this.RouterConfig = jsonConfig.RouterConfig this.InboundConfig = jsonConfig.InboundConfig @@ -63,10 +55,37 @@ func (this *Config) UnmarshalJSON(data []byte) error { return nil } -func (this *ConnectionConfig) UnmarshalJSON(data []byte) error { +func (this *InboundConnectionConfig) UnmarshalJSON(data []byte) error { + type JsonConfig struct { + Port uint16 `json:"port"` + Listen *v2net.AddressJson `json:"listen"` + Protocol string `json:"protocol"` + Settings json.RawMessage `json:"settings"` + } + + jsonConfig := new(JsonConfig) + if err := json.Unmarshal(data, jsonConfig); err != nil { + return err + } + this.Port = v2net.Port(jsonConfig.Port) + this.ListenOn = v2net.AnyIP + if jsonConfig.Listen != nil { + if jsonConfig.Listen.Address.IsDomain() { + return errors.New("Point: Unable to listen on domain address: " + jsonConfig.Listen.Address.Domain()) + } + this.ListenOn = jsonConfig.Listen.Address + } + + this.Protocol = jsonConfig.Protocol + this.Settings = jsonConfig.Settings + return nil +} + +func (this *OutboundConnectionConfig) UnmarshalJSON(data []byte) error { type JsonConnectionConfig struct { - Protocol string `json:"protocol"` - Settings json.RawMessage `json:"settings"` + Protocol string `json:"protocol"` + SendThrough *v2net.AddressJson `json:"sendThrough"` + Settings json.RawMessage `json:"settings"` } jsonConfig := new(JsonConnectionConfig) if err := json.Unmarshal(data, jsonConfig); err != nil { @@ -74,6 +93,14 @@ func (this *ConnectionConfig) UnmarshalJSON(data []byte) error { } this.Protocol = jsonConfig.Protocol this.Settings = jsonConfig.Settings + + if jsonConfig.SendThrough != nil { + address := jsonConfig.SendThrough.Address + if address.IsDomain() { + return errors.New("Point: Unable to send through: " + address.String()) + } + this.SendThrough = address + } return nil } @@ -173,9 +200,10 @@ func (this *InboundDetourConfig) UnmarshalJSON(data []byte) error { func (this *OutboundDetourConfig) UnmarshalJSON(data []byte) error { type JsonOutboundDetourConfig struct { - Protocol string `json:"protocol"` - Tag string `json:"tag"` - Settings json.RawMessage `json:"settings"` + Protocol string `json:"protocol"` + SendThrough *v2net.AddressJson `json:"sendThrough"` + Tag string `json:"tag"` + Settings json.RawMessage `json:"settings"` } jsonConfig := new(JsonOutboundDetourConfig) if err := json.Unmarshal(data, jsonConfig); err != nil { @@ -184,6 +212,14 @@ func (this *OutboundDetourConfig) UnmarshalJSON(data []byte) error { this.Protocol = jsonConfig.Protocol this.Tag = jsonConfig.Tag this.Settings = jsonConfig.Settings + + if jsonConfig.SendThrough != nil { + address := jsonConfig.SendThrough.Address + if address.IsDomain() { + return errors.New("Point: Unable to send through: " + address.String()) + } + this.SendThrough = address + } return nil } diff --git a/shell/point/config_json_test.go b/shell/point/config_json_test.go index 74d6a3d52..6e2351a14 100644 --- a/shell/point/config_json_test.go +++ b/shell/point/config_json_test.go @@ -23,8 +23,8 @@ func TestClientSampleConfig(t *testing.T) { pointConfig, err := LoadConfig(filepath.Join(baseDir, "vpoint_socks_vmess.json")) assert.Error(err).IsNil() - assert.Port(pointConfig.Port).IsValid() assert.Pointer(pointConfig.InboundConfig).IsNotNil() + assert.Port(pointConfig.InboundConfig.Port).IsValid() assert.Pointer(pointConfig.OutboundConfig).IsNotNil() assert.String(pointConfig.InboundConfig.Protocol).Equals("socks") @@ -43,8 +43,8 @@ func TestServerSampleConfig(t *testing.T) { pointConfig, err := LoadConfig(filepath.Join(baseDir, "vpoint_vmess_freedom.json")) assert.Error(err).IsNil() - assert.Port(pointConfig.Port).IsValid() assert.Pointer(pointConfig.InboundConfig).IsNotNil() + assert.Port(pointConfig.InboundConfig.Port).IsValid() assert.Pointer(pointConfig.OutboundConfig).IsNotNil() assert.String(pointConfig.InboundConfig.Protocol).Equals("vmess") diff --git a/shell/point/inbound_detour_always.go b/shell/point/inbound_detour_always.go index 984a05790..3de7c89c1 100644 --- a/shell/point/inbound_detour_always.go +++ b/shell/point/inbound_detour_always.go @@ -32,7 +32,7 @@ func NewInboundDetourHandlerAlways(space app.Space, config *InboundDetourConfig) handler.ich = make([]*InboundConnectionHandlerWithPort, 0, ports.To-ports.From+1) for i := ports.From; i <= ports.To; i++ { ichConfig := config.Settings - ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, ichConfig) + ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, ichConfig, config.ListenOn, i) if err != nil { log.Error("Failed to create inbound connection handler: ", err) return nil, err @@ -61,7 +61,7 @@ func (this *InboundDetourHandlerAlways) Close() { func (this *InboundDetourHandlerAlways) Start() error { for _, ich := range this.ich { err := retry.Timed(100 /* times */, 100 /* ms */).On(func() error { - err := ich.handler.Listen(ich.listen, ich.port) + err := ich.handler.Start() if err != nil { log.Error("Failed to start inbound detour on port ", ich.port, ": ", err) return err diff --git a/shell/point/inbound_detour_dynamic.go b/shell/point/inbound_detour_dynamic.go index d5e50d632..622fbf6bc 100644 --- a/shell/point/inbound_detour_dynamic.go +++ b/shell/point/inbound_detour_dynamic.go @@ -8,7 +8,6 @@ import ( "github.com/v2ray/v2ray-core/common/dice" "github.com/v2ray/v2ray-core/common/log" v2net "github.com/v2ray/v2ray-core/common/net" - "github.com/v2ray/v2ray-core/common/retry" "github.com/v2ray/v2ray-core/proxy" proxyrepo "github.com/v2ray/v2ray-core/proxy/repo" ) @@ -18,8 +17,7 @@ type InboundDetourHandlerDynamic struct { space app.Space config *InboundDetourConfig portsInUse map[v2net.Port]bool - ichInUse []proxy.InboundHandler - ich2Recycle []proxy.InboundHandler + ichs []proxy.InboundHandler lastRefresh time.Time } @@ -29,18 +27,16 @@ func NewInboundDetourHandlerDynamic(space app.Space, config *InboundDetourConfig config: config, portsInUse: make(map[v2net.Port]bool), } - ichCount := config.Allocation.Concurrency - ichArray := make([]proxy.InboundHandler, ichCount*2) - for idx := range ichArray { - ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, config.Settings) - if err != nil { - log.Error("Point: Failed to create inbound connection handler: ", err) - return nil, err - } - ichArray[idx] = ich + handler.ichs = make([]proxy.InboundHandler, config.Allocation.Concurrency) + + // To test configuration + ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, config.Settings, config.ListenOn, 0) + if err != nil { + log.Error("Point: Failed to create inbound connection handler: ", err) + return nil, err } - handler.ichInUse = ichArray[:ichCount] - handler.ich2Recycle = ichArray[ichCount:] + ich.Close() + return handler, nil } @@ -59,7 +55,7 @@ func (this *InboundDetourHandlerDynamic) pickUnusedPort() v2net.Port { func (this *InboundDetourHandlerDynamic) GetConnectionHandler() (proxy.InboundHandler, int) { this.RLock() defer this.RUnlock() - ich := this.ichInUse[dice.Roll(len(this.ichInUse))] + ich := this.ichs[dice.Roll(len(this.ichs))] until := this.config.Allocation.Refresh - int((time.Now().Unix()-this.lastRefresh.Unix())/60/1000) if until < 0 { until = 0 @@ -70,58 +66,68 @@ func (this *InboundDetourHandlerDynamic) GetConnectionHandler() (proxy.InboundHa func (this *InboundDetourHandlerDynamic) Close() { this.Lock() defer this.Unlock() - for _, ich := range this.ichInUse { + for _, ich := range this.ichs { ich.Close() } - if this.ich2Recycle != nil { - for _, ich := range this.ich2Recycle { - if ich != nil { - ich.Close() - } - } - } } func (this *InboundDetourHandlerDynamic) refresh() error { this.lastRefresh = time.Now() - for _, ich := range this.ich2Recycle { - port2Delete := ich.Port() + config := this.config + ich2Recycle := this.ichs + newIchs := make([]proxy.InboundHandler, config.Allocation.Concurrency) - ich.Close() - err := retry.Timed(100 /* times */, 1000 /* ms */).On(func() error { - port := this.pickUnusedPort() - err := ich.Listen(this.config.ListenOn, port) - if err != nil { - log.Error("Point: Failed to start inbound detour on port ", port, ": ", err) - return err - } - this.portsInUse[port] = true - return nil - }) + for idx, _ := range newIchs { + port := this.pickUnusedPort() + ich, err := proxyrepo.CreateInboundHandler(config.Protocol, this.space, config.Settings, config.ListenOn, port) if err != nil { - continue + log.Error("Point: Failed to create inbound connection handler: ", err) + return err } - - delete(this.portsInUse, port2Delete) + err = ich.Start() + if err != nil { + log.Error("Point: Failed to start inbound connection handler: ", err) + return err + } + this.portsInUse[port] = true + newIchs[idx] = ich } this.Lock() - this.ich2Recycle, this.ichInUse = this.ichInUse, this.ich2Recycle + this.ichs = newIchs this.Unlock() + go func() { + time.Sleep(time.Minute) + for _, ich := range ich2Recycle { + if ich == nil { + continue + } + port := ich.Port() + ich.Close() + delete(this.portsInUse, port) + } + ich2Recycle = nil + }() + return nil } func (this *InboundDetourHandlerDynamic) Start() error { err := this.refresh() if err != nil { + log.Error("Point: Failed to refresh dynamic allocations: ", err) return err } go func() { - for range time.Tick(time.Duration(this.config.Allocation.Refresh) * time.Minute) { - this.refresh() + for { + time.Sleep(time.Duration(this.config.Allocation.Refresh) * time.Minute) + err := this.refresh() + if err != nil { + log.Error("Point: Failed to refresh dynamic allocations: ", err) + } } }() diff --git a/shell/point/point.go b/shell/point/point.go index 17659bbb4..1b4226a42 100644 --- a/shell/point/point.go +++ b/shell/point/point.go @@ -35,8 +35,12 @@ type Point struct { // The server is not started at this point. func NewPoint(pConfig *Config) (*Point, error) { var vpoint = new(Point) - vpoint.port = pConfig.Port - vpoint.listen = pConfig.ListenOn + vpoint.port = pConfig.InboundConfig.Port + if vpoint.port == 0 { + vpoint.port = pConfig.Port // Backward compatibility + } + + vpoint.listen = pConfig.InboundConfig.ListenOn if pConfig.TransportConfig != nil { pConfig.TransportConfig.Apply() @@ -87,7 +91,7 @@ func NewPoint(pConfig *Config) (*Point, error) { vpoint.space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(vpoint.space)) ichConfig := pConfig.InboundConfig.Settings - ich, err := proxyrepo.CreateInboundHandler(pConfig.InboundConfig.Protocol, vpoint.space, ichConfig) + ich, err := proxyrepo.CreateInboundHandler(pConfig.InboundConfig.Protocol, vpoint.space, ichConfig, pConfig.InboundConfig.ListenOn, vpoint.port) if err != nil { log.Error("Failed to create inbound connection handler: ", err) return nil, err @@ -95,7 +99,7 @@ func NewPoint(pConfig *Config) (*Point, error) { vpoint.ich = ich ochConfig := pConfig.OutboundConfig.Settings - och, err := proxyrepo.CreateOutboundHandler(pConfig.OutboundConfig.Protocol, vpoint.space, ochConfig) + och, err := proxyrepo.CreateOutboundHandler(pConfig.OutboundConfig.Protocol, vpoint.space, ochConfig, pConfig.OutboundConfig.SendThrough) if err != nil { log.Error("Failed to create outbound connection handler: ", err) return nil, err @@ -140,7 +144,7 @@ func NewPoint(pConfig *Config) (*Point, error) { if len(outboundDetours) > 0 { vpoint.odh = make(map[string]proxy.OutboundHandler) for _, detourConfig := range outboundDetours { - detourHandler, err := proxyrepo.CreateOutboundHandler(detourConfig.Protocol, vpoint.space, detourConfig.Settings) + detourHandler, err := proxyrepo.CreateOutboundHandler(detourConfig.Protocol, vpoint.space, detourConfig.Settings, detourConfig.SendThrough) if err != nil { log.Error("Point: Failed to create detour outbound connection handler: ", err) return nil, err @@ -173,7 +177,7 @@ func (this *Point) Start() error { } err := retry.Timed(100 /* times */, 100 /* ms */).On(func() error { - err := this.ich.Listen(this.listen, this.port) + err := this.ich.Start() if err != nil { return err } diff --git a/testing/scenarios/data/test_1_client.json b/testing/scenarios/data/test_1_client.json index 5b3386917..8ffcef892 100644 --- a/testing/scenarios/data/test_1_client.json +++ b/testing/scenarios/data/test_1_client.json @@ -1,7 +1,7 @@ { - "port": 50000, - "listen": "127.0.0.1", "inbound": { + "port": 50000, + "listen": "127.0.0.1", "protocol": "socks", "settings": { "auth": "noauth", diff --git a/testing/scenarios/data/test_1_server.json b/testing/scenarios/data/test_1_server.json index 1d4f940bf..1c1eca73b 100644 --- a/testing/scenarios/data/test_1_server.json +++ b/testing/scenarios/data/test_1_server.json @@ -1,10 +1,10 @@ { "port": 50001, - "listen": "127.0.0.1", "log": { "loglevel": "none" }, "inbound": { + "listen": "127.0.0.1", "protocol": "vmess", "settings": { "clients": [ diff --git a/testing/scenarios/data/test_2_client.json b/testing/scenarios/data/test_2_client.json index e9e206497..489647283 100644 --- a/testing/scenarios/data/test_2_client.json +++ b/testing/scenarios/data/test_2_client.json @@ -1,7 +1,7 @@ { "port": 50010, - "listen": "127.0.0.1", "inbound": { + "listen": "127.0.0.1", "protocol": "socks", "settings": { "auth": "noauth", diff --git a/testing/scenarios/data/test_2_server.json b/testing/scenarios/data/test_2_server.json index 59ef59f0d..f77564419 100644 --- a/testing/scenarios/data/test_2_server.json +++ b/testing/scenarios/data/test_2_server.json @@ -1,10 +1,10 @@ { "port": 50017, - "listen": "127.0.0.1", "log": { "loglevel": "none" }, "inbound": { + "listen": "127.0.0.1", "protocol": "vmess", "settings": { "clients": [ diff --git a/testing/scenarios/data/test_3_client.json b/testing/scenarios/data/test_3_client.json index ea5627e0e..80d052c47 100644 --- a/testing/scenarios/data/test_3_client.json +++ b/testing/scenarios/data/test_3_client.json @@ -1,10 +1,10 @@ { "port": 50020, - "listen": "127.0.0.1", "log": { "loglevel": "none" }, "inbound": { + "listen": "127.0.0.1", "protocol": "dokodemo-door", "settings": { "address": "127.0.0.1", diff --git a/testing/scenarios/data/test_3_server.json b/testing/scenarios/data/test_3_server.json index 3345cea1f..0e38ad10e 100644 --- a/testing/scenarios/data/test_3_server.json +++ b/testing/scenarios/data/test_3_server.json @@ -1,7 +1,7 @@ { "port": 50021, - "listen": "127.0.0.1", "inbound": { + "listen": "127.0.0.1", "protocol": "vmess", "settings": { "clients": [ diff --git a/testing/scenarios/data/test_4_client.json b/testing/scenarios/data/test_4_client.json index f2c44b61e..94723c87a 100644 --- a/testing/scenarios/data/test_4_client.json +++ b/testing/scenarios/data/test_4_client.json @@ -1,10 +1,10 @@ { "port": 50030, - "listen": "127.0.0.1", "log": { "loglevel": "debug" }, "inbound": { + "listen": "127.0.0.1", "protocol": "dokodemo-door", "settings": { "address": "127.0.0.1", diff --git a/testing/scenarios/data/test_4_server.json b/testing/scenarios/data/test_4_server.json index a06d0b2ec..c2a2300c1 100644 --- a/testing/scenarios/data/test_4_server.json +++ b/testing/scenarios/data/test_4_server.json @@ -1,10 +1,10 @@ { "port": 50031, - "listen": "127.0.0.1", "log": { "loglevel": "debug" }, "inbound": { + "listen": "127.0.0.1", "protocol": "vmess", "settings": { "clients": [ diff --git a/testing/scenarios/data/test_5_client.json b/testing/scenarios/data/test_5_client.json index 6d5cb218d..111e1e46b 100644 --- a/testing/scenarios/data/test_5_client.json +++ b/testing/scenarios/data/test_5_client.json @@ -1,7 +1,7 @@ { "port": 50040, - "listen": "127.0.0.1", "inbound": { + "listen": "127.0.0.1", "protocol": "http", "settings": {} }, diff --git a/testing/scenarios/data/test_5_server.json b/testing/scenarios/data/test_5_server.json index f8e9149cb..487303b88 100644 --- a/testing/scenarios/data/test_5_server.json +++ b/testing/scenarios/data/test_5_server.json @@ -1,7 +1,7 @@ { "port": 50041, - "listen": "127.0.0.1", "inbound": { + "listen": "127.0.0.1", "protocol": "vmess", "settings": { "clients": [ diff --git a/testing/scenarios/data/test_6_server.json b/testing/scenarios/data/test_6_server.json index 2744bfd3b..fff9f381b 100644 --- a/testing/scenarios/data/test_6_server.json +++ b/testing/scenarios/data/test_6_server.json @@ -1,7 +1,7 @@ { "port": 50051, - "listen": "127.0.0.1", "inbound": { + "listen": "127.0.0.1", "protocol": "shadowsocks", "settings": { "method": "aes-256-cfb", diff --git a/testing/scenarios/server_env.go b/testing/scenarios/server_env.go index df946c569..8941552a0 100644 --- a/testing/scenarios/server_env.go +++ b/testing/scenarios/server_env.go @@ -1,12 +1,13 @@ package scenarios import ( + "io/ioutil" "os" + "os/exec" "path/filepath" _ "github.com/v2ray/v2ray-core/app/router/rules" "github.com/v2ray/v2ray-core/common/log" - "github.com/v2ray/v2ray-core/shell/point" // The following are necessary as they register handlers in their init functions. _ "github.com/v2ray/v2ray-core/proxy/blackhole" @@ -20,9 +21,25 @@ import ( ) var ( - runningServers = make([]*point.Point, 0, 10) + runningServers = make([]*exec.Cmd, 0, 10) + + binaryPath string ) +func BuildV2Ray() error { + if len(binaryPath) > 0 { + return nil + } + + dir, err := ioutil.TempDir("", "v2ray") + if err != nil { + return err + } + binaryPath = filepath.Join(dir, "v2ray.exe") + cmd := exec.Command("go", "build", "-tags=json", "-o="+binaryPath, filepath.Join("github.com", "v2ray", "v2ray-core", "release", "server")) + return cmd.Run() +} + func TestFile(filename string) string { return filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "v2ray", "v2ray-core", "testing", "scenarios", "data", filename) } @@ -46,31 +63,30 @@ func InitializeServerClient(testcase string) error { } func InitializeServer(configFile string) error { - config, err := point.LoadConfig(configFile) + err := BuildV2Ray() if err != nil { - log.Error("Failed to read config file (", configFile, "): ", configFile, err) return err } - vPoint, err := point.NewPoint(config) + proc := exec.Command(binaryPath, "-config="+configFile) + proc.Stderr = os.Stderr + proc.Stdout = os.Stdout + + err = proc.Start() if err != nil { - log.Error("Failed to create Point server: ", err) return err } - err = vPoint.Start() - if err != nil { - log.Error("Error starting Point server: ", err) - return err - } - runningServers = append(runningServers, vPoint) + runningServers = append(runningServers, proc) return nil } func CloseAllServers() { + log.Info("Closing all servers.") for _, server := range runningServers { - server.Close() + server.Process.Kill() } - runningServers = make([]*point.Point, 0, 10) + runningServers = make([]*exec.Cmd, 0, 10) + log.Info("All server closed.") } diff --git a/transport/hub/connection_cache.go b/transport/hub/connection_cache.go index 0d17ca116..55c4a6fa8 100644 --- a/transport/hub/connection_cache.go +++ b/transport/hub/connection_cache.go @@ -19,7 +19,7 @@ func (o *Once) Do(f func()) { o.m.Lock() defer o.m.Unlock() if o.done == 0 { - defer atomic.StoreUint32(&o.done, 1) + atomic.StoreUint32(&o.done, 1) f() } } diff --git a/transport/hub/dialer.go b/transport/hub/dialer.go index 6246c68c1..86ffa032b 100644 --- a/transport/hub/dialer.go +++ b/transport/hub/dialer.go @@ -15,49 +15,50 @@ var ( globalCache = NewConnectionCache() ) -func Dial(dest v2net.Destination) (*Connection, error) { - destStr := dest.String() +func Dial(src v2net.Address, dest v2net.Destination) (*Connection, error) { + if src == nil { + src = v2net.AnyIP + } + id := src.String() + "-" + dest.NetAddr() var conn net.Conn - if transport.IsConnectionReusable() { - conn = globalCache.Get(destStr) + if dest.IsTCP() && transport.IsConnectionReusable() { + conn = globalCache.Get(id) } if conn == nil { var err error - conn, err = DialWithoutCache(dest) + conn, err = DialWithoutCache(src, dest) if err != nil { return nil, err } } return &Connection{ - dest: destStr, + dest: id, conn: conn, listener: globalCache, }, nil } -func DialWithoutCache(dest v2net.Destination) (net.Conn, error) { - if dest.Address().IsDomain() { - dialer := &net.Dialer{ - Timeout: time.Second * 60, - DualStack: true, - } - network := "tcp" - if dest.IsUDP() { - network = "udp" - } - return dialer.Dial(network, dest.NetAddr()) +func DialWithoutCache(src v2net.Address, dest v2net.Destination) (net.Conn, error) { + dialer := &net.Dialer{ + Timeout: time.Second * 60, + DualStack: true, } - ip := dest.Address().IP() - if dest.IsTCP() { - return net.DialTCP("tcp", nil, &net.TCPAddr{ - IP: ip, - Port: int(dest.Port()), - }) + if src != nil && src != v2net.AnyIP { + var addr net.Addr + if dest.IsTCP() { + addr = &net.TCPAddr{ + IP: src.IP(), + Port: 0, + } + } else { + addr = &net.UDPAddr{ + IP: src.IP(), + Port: 0, + } + } + dialer.LocalAddr = addr } - return net.DialUDP("udp", nil, &net.UDPAddr{ - IP: ip, - Port: int(dest.Port()), - }) + return dialer.Dial(dest.Network().String(), dest.NetAddr()) } diff --git a/transport/hub/dialer_test.go b/transport/hub/dialer_test.go index cce772a09..24dd18f50 100644 --- a/transport/hub/dialer_test.go +++ b/transport/hub/dialer_test.go @@ -1,6 +1,7 @@ package hub_test import ( + "net" "testing" v2net "github.com/v2ray/v2ray-core/common/net" @@ -20,7 +21,35 @@ func TestDialDomain(t *testing.T) { assert.Error(err).IsNil() defer server.Close() - conn, err := Dial(v2net.TCPDestination(v2net.DomainAddress("local.v2ray.com"), dest.Port())) + conn, err := Dial(nil, v2net.TCPDestination(v2net.DomainAddress("local.v2ray.com"), dest.Port())) + assert.Error(err).IsNil() + assert.String(conn.RemoteAddr().String()).Equals("127.0.0.1:" + dest.Port().String()) + conn.Close() +} + +func TestDialWithLocalAddr(t *testing.T) { + assert := assert.On(t) + + server := &tcp.Server{ + Port: v2nettesting.PickPort(), + } + dest, err := server.Start() + assert.Error(err).IsNil() + defer server.Close() + + var localAddr net.IP + addrs, err := net.InterfaceAddrs() + assert.Error(err).IsNil() + for _, addr := range addrs { + str := addr.String() + ip := net.ParseIP(str) + if ip != nil && ip.To4() != nil { + localAddr = ip.To4() + } + } + assert.Pointer(localAddr).IsNotNil() + + conn, err := Dial(v2net.IPAddress(localAddr), v2net.TCPDestination(v2net.LocalHostIP, dest.Port())) assert.Error(err).IsNil() assert.String(conn.RemoteAddr().String()).Equals("127.0.0.1:" + dest.Port().String()) conn.Close() diff --git a/transport/hub/udp_server.go b/transport/hub/udp_server.go index 06b1a93ed..720472802 100644 --- a/transport/hub/udp_server.go +++ b/transport/hub/udp_server.go @@ -130,7 +130,7 @@ func (this *UDPServer) locateExistingAndDispatch(name string, payload *alloc.Buf } func (this *UDPServer) Dispatch(source v2net.Destination, destination v2net.Destination, payload *alloc.Buffer, callback UDPResponseCallback) { - destString := source.NetAddr() + "-" + destination.NetAddr() + destString := source.String() + "-" + destination.String() log.Debug("UDP Server: Dispatch request: ", destString) if this.locateExistingAndDispatch(destString, payload) { return