diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index ddeb9e707..96041dc8b 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -10,18 +10,20 @@ import ( "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" ) type DokodemoDoor struct { - tcpMutex sync.RWMutex - udpMutex sync.RWMutex - config *Config - accepting bool - address v2net.Address - port v2net.Port - space app.Space - tcpListener *net.TCPListener - udpConn *net.UDPConn + tcpMutex sync.RWMutex + udpMutex sync.RWMutex + config *Config + accepting bool + address v2net.Address + port v2net.Port + space app.Space + tcpListener *net.TCPListener + udpConn *net.UDPConn + listeningPort v2net.Port } func NewDokodemoDoor(space app.Space, config *Config) *DokodemoDoor { @@ -33,6 +35,10 @@ func NewDokodemoDoor(space app.Space, config *Config) *DokodemoDoor { } } +func (this *DokodemoDoor) Port() v2net.Port { + return this.listeningPort +} + func (this *DokodemoDoor) Close() { this.accepting = false if this.tcpListener != nil { @@ -50,6 +56,14 @@ func (this *DokodemoDoor) Close() { } func (this *DokodemoDoor) Listen(port v2net.Port) error { + if this.accepting { + if this.listeningPort == port { + return nil + } else { + return proxy.ErrorAlreadyListening + } + } + this.listeningPort = port this.accepting = true if this.config.Network.HasNetwork(v2net.TCPNetwork) { diff --git a/proxy/errors.go b/proxy/errors.go index 4eb105696..532078e31 100644 --- a/proxy/errors.go +++ b/proxy/errors.go @@ -7,4 +7,5 @@ import ( var ( InvalidAuthentication = errors.New("Invalid authentication.") InvalidProtocolVersion = errors.New("Invalid protocol version.") + ErrorAlreadyListening = errors.New("Already listening on another port.") ) diff --git a/proxy/http/http.go b/proxy/http/http.go index 7d1221407..79427be71 100644 --- a/proxy/http/http.go +++ b/proxy/http/http.go @@ -15,15 +15,17 @@ import ( v2net "github.com/v2ray/v2ray-core/common/net" "github.com/v2ray/v2ray-core/common/retry" "github.com/v2ray/v2ray-core/common/serial" + "github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/transport/ray" ) type HttpProxyServer struct { sync.Mutex - accepting bool - space app.Space - config *Config - tcpListener *net.TCPListener + accepting bool + space app.Space + config *Config + tcpListener *net.TCPListener + listeningPort v2net.Port } func NewHttpProxyServer(space app.Space, config *Config) *HttpProxyServer { @@ -33,6 +35,10 @@ func NewHttpProxyServer(space app.Space, config *Config) *HttpProxyServer { } } +func (this *HttpProxyServer) Port() v2net.Port { + return this.listeningPort +} + func (this *HttpProxyServer) Close() { this.accepting = false if this.tcpListener != nil { @@ -44,6 +50,15 @@ func (this *HttpProxyServer) Close() { } func (this *HttpProxyServer) Listen(port v2net.Port) error { + if this.accepting { + if this.listeningPort == port { + return nil + } else { + return proxy.ErrorAlreadyListening + } + } + this.listeningPort = port + tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{ Port: int(port.Value()), IP: []byte{0, 0, 0, 0}, diff --git a/proxy/proxy.go b/proxy/proxy.go index a4d764bc4..d488fa7fb 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -13,6 +13,8 @@ type InboundConnectionHandler interface { Listen(port v2net.Port) error // Close stops the handler to accepting anymore inbound connections. Close() + // Port returns the port that the handler is listening on. + Port() v2net.Port } // An OutboundConnectionHandler handles outbound network connection for V2Ray. diff --git a/proxy/socks/socks.go b/proxy/socks/socks.go index 1046ad4eb..92251b3bf 100644 --- a/proxy/socks/socks.go +++ b/proxy/socks/socks.go @@ -23,14 +23,15 @@ var ( // SocksServer is a SOCKS 5 proxy server type SocksServer struct { - tcpMutex sync.RWMutex - udpMutex sync.RWMutex - accepting bool - space app.Space - config *Config - tcpListener *net.TCPListener - udpConn *net.UDPConn - udpAddress v2net.Destination + tcpMutex sync.RWMutex + udpMutex sync.RWMutex + accepting bool + space app.Space + config *Config + tcpListener *net.TCPListener + udpConn *net.UDPConn + udpAddress v2net.Destination + listeningPort v2net.Port } func NewSocksServer(space app.Space, config *Config) *SocksServer { @@ -40,6 +41,10 @@ func NewSocksServer(space app.Space, config *Config) *SocksServer { } } +func (this *SocksServer) Port() v2net.Port { + return this.listeningPort +} + func (this *SocksServer) Close() { this.accepting = false if this.tcpListener != nil { @@ -57,6 +62,15 @@ func (this *SocksServer) Close() { } func (this *SocksServer) Listen(port v2net.Port) error { + if this.accepting { + if this.listeningPort == port { + return nil + } else { + return proxy.ErrorAlreadyListening + } + } + this.listeningPort = port + listener, err := net.ListenTCP("tcp", &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, Port: int(port), diff --git a/proxy/testing/mocks/inboundhandler.go b/proxy/testing/mocks/inboundhandler.go index d38b87843..f9416215a 100644 --- a/proxy/testing/mocks/inboundhandler.go +++ b/proxy/testing/mocks/inboundhandler.go @@ -9,17 +9,21 @@ import ( ) type InboundConnectionHandler struct { - Port v2net.Port + port v2net.Port Space app.Space ConnInput io.Reader ConnOutput io.Writer } func (this *InboundConnectionHandler) Listen(port v2net.Port) error { - this.Port = port + this.port = port return nil } +func (this *InboundConnectionHandler) Port() v2net.Port { + return this.port +} + func (this *InboundConnectionHandler) Close() { } diff --git a/proxy/vmess/command/accounts.go b/proxy/vmess/command/accounts.go index 29a943753..b3835c40a 100644 --- a/proxy/vmess/command/accounts.go +++ b/proxy/vmess/command/accounts.go @@ -2,7 +2,6 @@ package command import ( "io" - "time" v2net "github.com/v2ray/v2ray-core/common/net" "github.com/v2ray/v2ray-core/common/serial" @@ -22,44 +21,33 @@ func init() { // 2 bytes: alterid // 8 bytes: time type SwitchAccount struct { - Host v2net.Address - Port v2net.Port - ID *uuid.UUID - AlterIds serial.Uint16Literal - ValidUntil time.Time + Host v2net.Address + Port v2net.Port + ID *uuid.UUID + AlterIds serial.Uint16Literal + ValidSec serial.Uint16Literal } -func (this *SwitchAccount) Marshal(writer io.Writer) (int, error) { - outBytes := 0 +func (this *SwitchAccount) Marshal(writer io.Writer) { hostStr := "" if this.Host != nil { hostStr = this.Host.String() } writer.Write([]byte{byte(len(hostStr))}) - outBytes++ if len(hostStr) > 0 { writer.Write([]byte(hostStr)) - outBytes += len(hostStr) } writer.Write(this.Port.Bytes()) - outBytes += 2 idBytes := this.ID.Bytes() writer.Write(idBytes) - outBytes += len(idBytes) writer.Write(this.AlterIds.Bytes()) - outBytes += 2 - - timestamp := this.ValidUntil.Unix() - timeBytes := serial.Int64Literal(timestamp).Bytes() + timeBytes := this.ValidSec.Bytes() writer.Write(timeBytes) - outBytes += len(timeBytes) - - return outBytes, nil } func (this *SwitchAccount) Unmarshal(data []byte) error { @@ -84,9 +72,9 @@ func (this *SwitchAccount) Unmarshal(data []byte) error { } this.AlterIds = serial.ParseUint16(data[alterIdStart : alterIdStart+2]) timeStart := alterIdStart + 2 - if len(data) < timeStart+8 { + if len(data) < timeStart+2 { return transport.CorruptedPacket } - this.ValidUntil = time.Unix(serial.BytesLiteral(data[timeStart:timeStart+8]).Int64Value(), 0) + this.ValidSec = serial.ParseUint16(data[timeStart : timeStart+2]) return nil } diff --git a/proxy/vmess/command/accounts_test.go b/proxy/vmess/command/accounts_test.go index a699e5f95..e7c25895d 100644 --- a/proxy/vmess/command/accounts_test.go +++ b/proxy/vmess/command/accounts_test.go @@ -3,7 +3,6 @@ package command_test import ( "bytes" "testing" - "time" netassert "github.com/v2ray/v2ray-core/common/net/testing/assert" "github.com/v2ray/v2ray-core/common/uuid" @@ -16,19 +15,17 @@ func TestSwitchAccount(t *testing.T) { v2testing.Current(t) sa := &SwitchAccount{ - Port: 1234, - ID: uuid.New(), - AlterIds: 1024, - ValidUntil: time.Now(), + Port: 1234, + ID: uuid.New(), + AlterIds: 1024, + ValidSec: 8080, } cmd, err := CreateResponseCommand(1) assert.Error(err).IsNil() buffer := bytes.NewBuffer(make([]byte, 0, 1024)) - nBytes, err := sa.Marshal(buffer) - assert.Error(err).IsNil() - assert.Int(nBytes).Equals(buffer.Len()) + sa.Marshal(buffer) cmd.Unmarshal(buffer.Bytes()) sa2, ok := cmd.(*SwitchAccount) @@ -36,5 +33,5 @@ func TestSwitchAccount(t *testing.T) { netassert.Port(sa.Port).Equals(sa2.Port) assert.String(sa.ID).Equals(sa2.ID.String()) assert.Uint16(sa.AlterIds.Value()).Equals(sa2.AlterIds.Value()) - assert.Int64(sa.ValidUntil.Unix()).Equals(sa2.ValidUntil.Unix()) + assert.Uint16(sa.ValidSec.Value()).Equals(sa2.ValidSec.Value()) } diff --git a/proxy/vmess/command/command.go b/proxy/vmess/command/command.go index 08ced4881..d3fd37662 100644 --- a/proxy/vmess/command/command.go +++ b/proxy/vmess/command/command.go @@ -10,7 +10,7 @@ var ( ) type Command interface { - Marshal(io.Writer) (int, error) + Marshal(io.Writer) Unmarshal([]byte) error } diff --git a/proxy/vmess/command/dns.go b/proxy/vmess/command/dns.go index 4e4a5e5d3..75ee38f96 100644 --- a/proxy/vmess/command/dns.go +++ b/proxy/vmess/command/dns.go @@ -26,20 +26,16 @@ type CacheDns struct { Address v2net.Address } -func (this *CacheDns) Marshal(writer io.Writer) (int, error) { +func (this *CacheDns) Marshal(writer io.Writer) { if this.Address.IsIPv4() { writer.Write([]byte{typeIPv4}) writer.Write(this.Address.IP()) - return 5, nil } if this.Address.IsIPv6() { writer.Write([]byte{typeIPv6}) writer.Write(this.Address.IP()) - return 17, nil } - - return 0, ErrDomainAddress } func (this *CacheDns) Unmarshal(data []byte) error { diff --git a/proxy/vmess/command/dns_test.go b/proxy/vmess/command/dns_test.go index ee1390adb..beb897840 100644 --- a/proxy/vmess/command/dns_test.go +++ b/proxy/vmess/command/dns_test.go @@ -21,12 +21,10 @@ func TestCacheDnsIPv4(t *testing.T) { buffer := alloc.NewBuffer().Clear() defer buffer.Release() - nBytes, err := cd.Marshal(buffer) - assert.Error(err).IsNil() - assert.Int(nBytes).Equals(buffer.Len()) + cd.Marshal(buffer) cd2 := &CacheDns{} - err = cd2.Unmarshal(buffer.Value) + err := cd2.Unmarshal(buffer.Value) assert.Error(err).IsNil() netassert.Address(cd.Address).Equals(cd2.Address) } diff --git a/proxy/vmess/inbound/command.go b/proxy/vmess/inbound/command.go new file mode 100644 index 000000000..f6130c89c --- /dev/null +++ b/proxy/vmess/inbound/command.go @@ -0,0 +1,45 @@ +package inbound + +import ( + "github.com/v2ray/v2ray-core/common/alloc" + "github.com/v2ray/v2ray-core/common/serial" + "github.com/v2ray/v2ray-core/proxy/vmess/command" +) + +func (this *VMessInboundHandler) generateCommand(buffer *alloc.Buffer) { + cmd := byte(0) + commandBytes := alloc.NewSmallBuffer().Clear() + defer commandBytes.Release() + + if this.features != nil && this.features.Detour != nil { + tag := this.features.Detour.ToTag + if this.space.HasInboundHandlerManager() { + handlerManager := this.space.InboundHandlerManager() + handler, availableSec := handlerManager.GetHandler(tag) + inboundHandler, ok := handler.(*VMessInboundHandler) + if ok { + user := inboundHandler.GetUser() + availableSecUint16 := uint16(65535) + if availableSec < 65535 { + availableSecUint16 = uint16(availableSec) + } + + saCmd := &command.SwitchAccount{ + Port: inboundHandler.Port(), + ID: user.ID.UUID(), + AlterIds: serial.Uint16Literal(len(user.AlterIDs)), + ValidSec: serial.Uint16Literal(availableSecUint16), + } + saCmd.Marshal(commandBytes) + } + + } + } + + if commandBytes.Len() > 256 { + buffer.AppendBytes(byte(0), byte(0)) + } else { + buffer.AppendBytes(cmd, byte(commandBytes.Len())) + buffer.Append(commandBytes.Value) + } +} diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index d35259988..6fe76ee4e 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -22,17 +22,17 @@ import ( // Inbound connection handler that handles messages in VMess format. type VMessInboundHandler struct { sync.Mutex - space app.Space - clients protocol.UserSet - accepting bool - listener *net.TCPListener + space app.Space + clients protocol.UserSet + user *vmess.User + accepting bool + listener *net.TCPListener + features *FeaturesConfig + listeningPort v2net.Port } -func NewVMessInboundHandler(space app.Space, clients protocol.UserSet) *VMessInboundHandler { - return &VMessInboundHandler{ - space: space, - clients: clients, - } +func (this *VMessInboundHandler) Port() v2net.Port { + return this.listeningPort } func (this *VMessInboundHandler) Close() { @@ -45,11 +45,20 @@ func (this *VMessInboundHandler) Close() { } } -func (this *VMessInboundHandler) AddUser(user vmess.User) { - +func (this *VMessInboundHandler) GetUser() *vmess.User { + return this.user } func (this *VMessInboundHandler) Listen(port v2net.Port) error { + if this.accepting { + if this.listeningPort == port { + return nil + } else { + return proxy.ErrorAlreadyListening + } + } + this.listeningPort = port + listener, err := net.ListenTCP("tcp", &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, Port: int(port), @@ -175,6 +184,11 @@ func init() { allowedClients.AddUser(user) } - return NewVMessInboundHandler(space, allowedClients), nil + return &VMessInboundHandler{ + space: space, + clients: allowedClients, + features: config.Features, + user: config.AllowedUsers[0], + }, nil }) }