Parcourir la source

massive refactoring against json config parsing

v2ray il y a 9 ans
Parent
commit
e5fa96f814
51 fichiers modifiés avec 424 ajouts et 586 suppressions
  1. 1 1
      proxy/blackhole/config.go
  2. 14 0
      proxy/blackhole/config_json.go
  3. 0 15
      proxy/blackhole/json/json.go
  4. 5 5
      proxy/dokodemo/config.go
  5. 33 0
      proxy/dokodemo/config_json.go
  6. 7 7
      proxy/dokodemo/dokodemo.go
  7. 1 1
      proxy/dokodemo/dokodemo_factory.go
  8. 1 2
      proxy/dokodemo/dokodemo_test.go
  9. 0 37
      proxy/dokodemo/json/json.go
  10. 1 1
      proxy/freedom/config.go
  11. 14 0
      proxy/freedom/config_json.go
  12. 0 1
      proxy/freedom/freedom_test.go
  13. 0 15
      proxy/freedom/json/json.go
  14. 1 1
      proxy/http/config.go
  15. 14 0
      proxy/http/config_json.go
  16. 0 15
      proxy/http/json/json.go
  17. 0 16
      proxy/internal/config/json/json.go
  18. 22 7
      proxy/socks/config.go
  19. 64 0
      proxy/socks/config_json.go
  20. 22 0
      proxy/socks/config_json_test.go
  21. 0 93
      proxy/socks/json/config.go
  22. 0 65
      proxy/socks/json/config_test.go
  23. 6 6
      proxy/socks/socks.go
  24. 1 2
      proxy/socks/socks_test.go
  25. 1 1
      proxy/socks/socksfactory.go
  26. 1 1
      proxy/socks/udp.go
  27. 2 2
      proxy/vmess/inbound/config.go
  28. 26 0
      proxy/vmess/inbound/config_json.go
  29. 3 3
      proxy/vmess/inbound/inbound.go
  30. 0 26
      proxy/vmess/inbound/json/inbound.go
  31. 0 73
      proxy/vmess/json/user.go
  32. 2 2
      proxy/vmess/outbound/config.go
  33. 39 0
      proxy/vmess/outbound/config_json.go
  34. 0 85
      proxy/vmess/outbound/json/outbound.go
  35. 2 2
      proxy/vmess/outbound/outbound.go
  36. 2 2
      proxy/vmess/outbound/receiver.go
  37. 36 0
      proxy/vmess/outbound/receiver_json.go
  38. 9 7
      proxy/vmess/outbound/receiver_json_test.go
  39. 1 1
      proxy/vmess/protocol/rand_test.go
  40. 3 3
      proxy/vmess/protocol/testing/mockuserset.go
  41. 5 23
      proxy/vmess/protocol/testing/static_userset.go
  42. 8 8
      proxy/vmess/protocol/userset.go
  43. 3 3
      proxy/vmess/protocol/vmess.go
  44. 7 28
      proxy/vmess/protocol/vmess_test.go
  45. 19 5
      proxy/vmess/user.go
  46. 42 0
      proxy/vmess/user_json.go
  47. 0 2
      proxy/vmess/vmess_test.go
  48. 0 7
      release/server/main.go
  49. 5 5
      shell/point/json/json_test.go
  50. 0 6
      testing/scenarios/server_env.go
  51. 1 1
      tools/build/go.go

+ 1 - 1
proxy/blackhole/config.go

@@ -1,4 +1,4 @@
 package blackhole
 
-type Config interface {
+type Config struct {
 }

+ 14 - 0
proxy/blackhole/config_json.go

@@ -0,0 +1,14 @@
+// +build json
+
+package blackhole
+
+import (
+	"github.com/v2ray/v2ray-core/proxy/internal/config"
+)
+
+func init() {
+	config.RegisterOutboundConnectionConfig("blackhole",
+		func(data []byte) (interface{}, error) {
+			return new(Config), nil
+		})
+}

+ 0 - 15
proxy/blackhole/json/json.go

@@ -1,15 +0,0 @@
-package json
-
-import (
-	"github.com/v2ray/v2ray-core/proxy/internal/config"
-	"github.com/v2ray/v2ray-core/proxy/internal/config/json"
-)
-
-type BlackHoleConfig struct {
-}
-
-func init() {
-	config.RegisterOutboundConnectionConfig("blackhole", json.JsonConfigLoader(func() interface{} {
-		return new(BlackHoleConfig)
-	}))
-}

+ 5 - 5
proxy/dokodemo/config.go

@@ -4,9 +4,9 @@ import (
 	v2net "github.com/v2ray/v2ray-core/common/net"
 )
 
-type Config interface {
-	Address() v2net.Address
-	Port() v2net.Port
-	Network() v2net.NetworkList
-	Timeout() int
+type Config struct {
+	Address v2net.Address
+	Port    v2net.Port
+	Network v2net.NetworkList
+	Timeout int
 }

+ 33 - 0
proxy/dokodemo/config_json.go

@@ -0,0 +1,33 @@
+// +build json
+
+package dokodemo
+
+import (
+	"encoding/json"
+
+	v2net "github.com/v2ray/v2ray-core/common/net"
+	v2netjson "github.com/v2ray/v2ray-core/common/net/json"
+	"github.com/v2ray/v2ray-core/proxy/internal/config"
+)
+
+func init() {
+	config.RegisterInboundConnectionConfig("dokodemo-door",
+		func(data []byte) (interface{}, error) {
+			type DokodemoConfig struct {
+				Host         *v2netjson.Host        `json:"address"`
+				PortValue    v2net.Port             `json:"port"`
+				NetworkList  *v2netjson.NetworkList `json:"network"`
+				TimeoutValue int                    `json:"timeout"`
+			}
+			rawConfig := new(DokodemoConfig)
+			if err := json.Unmarshal(data, rawConfig); err != nil {
+				return nil, err
+			}
+			return &Config{
+				Address: rawConfig.Host.Address(),
+				Port:    rawConfig.PortValue,
+				Network: rawConfig.NetworkList,
+				Timeout: rawConfig.TimeoutValue,
+			}, nil
+		})
+}

+ 7 - 7
proxy/dokodemo/dokodemo.go

@@ -15,7 +15,7 @@ import (
 type DokodemoDoor struct {
 	tcpMutex    sync.RWMutex
 	udpMutex    sync.RWMutex
-	config      Config
+	config      *Config
 	accepting   bool
 	address     v2net.Address
 	port        v2net.Port
@@ -24,12 +24,12 @@ type DokodemoDoor struct {
 	udpConn     *net.UDPConn
 }
 
-func NewDokodemoDoor(space app.Space, config Config) *DokodemoDoor {
+func NewDokodemoDoor(space app.Space, config *Config) *DokodemoDoor {
 	return &DokodemoDoor{
 		config:  config,
 		space:   space,
-		address: config.Address(),
-		port:    config.Port(),
+		address: config.Address,
+		port:    config.Port,
 	}
 }
 
@@ -52,13 +52,13 @@ func (this *DokodemoDoor) Close() {
 func (this *DokodemoDoor) Listen(port v2net.Port) error {
 	this.accepting = true
 
-	if this.config.Network().HasNetwork(v2net.TCPNetwork) {
+	if this.config.Network.HasNetwork(v2net.TCPNetwork) {
 		err := this.ListenTCP(port)
 		if err != nil {
 			return err
 		}
 	}
-	if this.config.Network().HasNetwork(v2net.UDPNetwork) {
+	if this.config.Network.HasNetwork(v2net.UDPNetwork) {
 		err := this.ListenUDP(port)
 		if err != nil {
 			return err
@@ -163,7 +163,7 @@ func (this *DokodemoDoor) HandleTCPConnection(conn *net.TCPConn) {
 	inputFinish.Lock()
 	outputFinish.Lock()
 
-	reader := v2net.NewTimeOutReader(this.config.Timeout(), conn)
+	reader := v2net.NewTimeOutReader(this.config.Timeout, conn)
 	go dumpInput(reader, ray.InboundInput(), &inputFinish)
 	go dumpOutput(conn, ray.InboundOutput(), &outputFinish)
 

+ 1 - 1
proxy/dokodemo/dokodemo_factory.go

@@ -9,7 +9,7 @@ import (
 func init() {
 	internal.MustRegisterInboundConnectionHandlerCreator("dokodemo-door",
 		func(space app.Space, rawConfig interface{}) (proxy.InboundConnectionHandler, error) {
-			config := rawConfig.(Config)
+			config := rawConfig.(*Config)
 			return NewDokodemoDoor(space, config), nil
 		})
 }

+ 1 - 2
proxy/dokodemo/dokodemo_test.go

@@ -1,11 +1,10 @@
-package dokodemo
+package dokodemo_test
 
 import (
 	"net"
 	"testing"
 
 	v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
-	_ "github.com/v2ray/v2ray-core/proxy/dokodemo/json"
 	_ "github.com/v2ray/v2ray-core/proxy/freedom"
 	"github.com/v2ray/v2ray-core/shell/point"
 	"github.com/v2ray/v2ray-core/shell/point/testing/mocks"

+ 0 - 37
proxy/dokodemo/json/json.go

@@ -1,37 +0,0 @@
-package json
-
-import (
-	v2net "github.com/v2ray/v2ray-core/common/net"
-	v2netjson "github.com/v2ray/v2ray-core/common/net/json"
-	"github.com/v2ray/v2ray-core/proxy/internal/config"
-	"github.com/v2ray/v2ray-core/proxy/internal/config/json"
-)
-
-type DokodemoConfig struct {
-	Host         *v2netjson.Host        `json:"address"`
-	PortValue    v2net.Port             `json:"port"`
-	NetworkList  *v2netjson.NetworkList `json:"network"`
-	TimeoutValue int                    `json:"timeout"`
-}
-
-func (this *DokodemoConfig) Address() v2net.Address {
-	return this.Host.Address()
-}
-
-func (this *DokodemoConfig) Port() v2net.Port {
-	return this.PortValue
-}
-
-func (this *DokodemoConfig) Network() v2net.NetworkList {
-	return this.NetworkList
-}
-
-func (this *DokodemoConfig) Timeout() int {
-	return this.TimeoutValue
-}
-
-func init() {
-	config.RegisterInboundConnectionConfig("dokodemo-door", json.JsonConfigLoader(func() interface{} {
-		return new(DokodemoConfig)
-	}))
-}

+ 1 - 1
proxy/freedom/config.go

@@ -1,4 +1,4 @@
 package freedom
 
-type Config interface {
+type Config struct {
 }

+ 14 - 0
proxy/freedom/config_json.go

@@ -0,0 +1,14 @@
+// +build json
+
+package freedom
+
+import (
+	"github.com/v2ray/v2ray-core/proxy/internal/config"
+)
+
+func init() {
+	config.RegisterOutboundConnectionConfig("freedom",
+		func(data []byte) (interface{}, error) {
+			return new(Config), nil
+		})
+}

+ 0 - 1
proxy/freedom/freedom_test.go

@@ -14,7 +14,6 @@ import (
 	v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
 	v2proxy "github.com/v2ray/v2ray-core/proxy"
 	_ "github.com/v2ray/v2ray-core/proxy/socks"
-	_ "github.com/v2ray/v2ray-core/proxy/socks/json"
 	proxytesting "github.com/v2ray/v2ray-core/proxy/testing"
 	proxymocks "github.com/v2ray/v2ray-core/proxy/testing/mocks"
 	"github.com/v2ray/v2ray-core/shell/point"

+ 0 - 15
proxy/freedom/json/json.go

@@ -1,15 +0,0 @@
-package json
-
-import (
-	"github.com/v2ray/v2ray-core/proxy/internal/config"
-	"github.com/v2ray/v2ray-core/proxy/internal/config/json"
-)
-
-type FreedomConfiguration struct {
-}
-
-func init() {
-	config.RegisterOutboundConnectionConfig("freedom", json.JsonConfigLoader(func() interface{} {
-		return &FreedomConfiguration{}
-	}))
-}

+ 1 - 1
proxy/http/config.go

@@ -1,4 +1,4 @@
 package http
 
-type Config interface {
+type Config struct {
 }

+ 14 - 0
proxy/http/config_json.go

@@ -0,0 +1,14 @@
+// +build json
+
+package http
+
+import (
+	"github.com/v2ray/v2ray-core/proxy/internal/config"
+)
+
+func init() {
+	config.RegisterInboundConnectionConfig("http",
+		func(data []byte) (interface{}, error) {
+			return new(Config), nil
+		})
+}

+ 0 - 15
proxy/http/json/json.go

@@ -1,15 +0,0 @@
-package json
-
-import (
-	"github.com/v2ray/v2ray-core/proxy/internal/config"
-	"github.com/v2ray/v2ray-core/proxy/internal/config/json"
-)
-
-type HttpProxyConfig struct {
-}
-
-func init() {
-	config.RegisterInboundConnectionConfig("http", json.JsonConfigLoader(func() interface{} {
-		return new(HttpProxyConfig)
-	}))
-}

+ 0 - 16
proxy/internal/config/json/json.go

@@ -1,16 +0,0 @@
-package json
-
-import (
-	"encoding/json"
-
-	"github.com/v2ray/v2ray-core/common/log"
-)
-
-func JsonConfigLoader(newConfig func() interface{}) func(data []byte) (interface{}, error) {
-	return func(data []byte) (interface{}, error) {
-		obj := newConfig()
-		log.Debug("Unmarshalling JSON: %s", string(data))
-		err := json.Unmarshal(data, obj)
-		return obj, err
-	}
-}

+ 22 - 7
proxy/socks/config.go

@@ -1,13 +1,28 @@
 package socks
 
 import (
-	"net"
+	v2net "github.com/v2ray/v2ray-core/common/net"
 )
 
-type Config interface {
-	IsNoAuth() bool
-	IsPassword() bool
-	HasAccount(username, password string) bool
-	IP() net.IP
-	UDPEnabled() bool
+const (
+	AuthTypeNoAuth   = byte(0)
+	AuthTypePassword = byte(1)
+)
+
+type Config struct {
+	AuthType   byte
+	Accounts   map[string]string
+	Address    v2net.Address
+	UDPEnabled bool
+}
+
+func (this *Config) HasAccount(username, password string) bool {
+	if this.Accounts == nil {
+		return false
+	}
+	storedPassed, found := this.Accounts[username]
+	if !found {
+		return false
+	}
+	return storedPassed == password
 }

+ 64 - 0
proxy/socks/config_json.go

@@ -0,0 +1,64 @@
+// +build json
+
+package socks
+
+import (
+	"encoding/json"
+
+	"github.com/v2ray/v2ray-core/common/log"
+	v2net "github.com/v2ray/v2ray-core/common/net"
+	v2netjson "github.com/v2ray/v2ray-core/common/net/json"
+	"github.com/v2ray/v2ray-core/proxy/internal"
+	"github.com/v2ray/v2ray-core/proxy/internal/config"
+)
+
+const (
+	AuthMethodNoAuth   = "noauth"
+	AuthMethodUserPass = "password"
+)
+
+func init() {
+	config.RegisterInboundConnectionConfig("socks",
+		func(data []byte) (interface{}, error) {
+			type SocksAccount struct {
+				Username string `json:"user"`
+				Password string `json:"pass"`
+			}
+
+			type SocksConfig struct {
+				AuthMethod string          `json:"auth"`
+				Accounts   []*SocksAccount `json:"accounts"`
+				UDP        bool            `json:"udp"`
+				Host       *v2netjson.Host `json:"ip"`
+			}
+
+			rawConfig := new(SocksConfig)
+			if err := json.Unmarshal(data, rawConfig); err != nil {
+				return nil, err
+			}
+			socksConfig := new(Config)
+			if rawConfig.AuthMethod == AuthMethodNoAuth {
+				socksConfig.AuthType = AuthTypeNoAuth
+			} else if rawConfig.AuthMethod == AuthMethodUserPass {
+				socksConfig.AuthType = AuthTypePassword
+			} else {
+				log.Error("Socks: Unknown auth method: %s", rawConfig.AuthMethod)
+				return nil, internal.ErrorBadConfiguration
+			}
+
+			if len(rawConfig.Accounts) > 0 {
+				socksConfig.Accounts = make(map[string]string, len(rawConfig.Accounts))
+				for _, account := range rawConfig.Accounts {
+					socksConfig.Accounts[account.Username] = account.Password
+				}
+			}
+
+			socksConfig.UDPEnabled = rawConfig.UDP
+			if rawConfig.Host != nil {
+				socksConfig.Address = rawConfig.Host.Address()
+			} else {
+				socksConfig.Address = v2net.IPAddress([]byte{127, 0, 0, 1})
+			}
+			return socksConfig, nil
+		})
+}

+ 22 - 0
proxy/socks/config_json_test.go

@@ -0,0 +1,22 @@
+// +build json
+
+package socks_test
+
+import (
+	"testing"
+
+	"github.com/v2ray/v2ray-core/proxy/internal/config"
+	"github.com/v2ray/v2ray-core/proxy/socks"
+	v2testing "github.com/v2ray/v2ray-core/testing"
+	"github.com/v2ray/v2ray-core/testing/assert"
+)
+
+func TestDefaultIPAddress(t *testing.T) {
+	v2testing.Current(t)
+
+	socksConfig, err := config.CreateInboundConnectionConfig("socks", []byte(`{
+    "auth": "noauth"
+  }`))
+	assert.Error(err).IsNil()
+	assert.String(socksConfig.(*socks.Config).Address).Equals("127.0.0.1")
+}

+ 0 - 93
proxy/socks/json/config.go

@@ -1,93 +0,0 @@
-package json
-
-import (
-	"encoding/json"
-	"errors"
-	"net"
-
-	"github.com/v2ray/v2ray-core/proxy/internal/config"
-	jsonconfig "github.com/v2ray/v2ray-core/proxy/internal/config/json"
-)
-
-const (
-	AuthMethodNoAuth   = "noauth"
-	AuthMethodUserPass = "password"
-)
-
-type SocksAccount struct {
-	Username string `json:"user"`
-	Password string `json:"pass"`
-}
-
-type SocksAccountMap map[string]string
-
-func (this *SocksAccountMap) UnmarshalJSON(data []byte) error {
-	var accounts []SocksAccount
-	err := json.Unmarshal(data, &accounts)
-	if err != nil {
-		return err
-	}
-	*this = make(map[string]string)
-	for _, account := range accounts {
-		(*this)[account.Username] = account.Password
-	}
-	return nil
-}
-
-func (this *SocksAccountMap) HasAccount(user, pass string) bool {
-	if actualPass, found := (*this)[user]; found {
-		return actualPass == pass
-	}
-	return false
-}
-
-type IPAddress net.IP
-
-func (this *IPAddress) UnmarshalJSON(data []byte) error {
-	var ipStr string
-	err := json.Unmarshal(data, &ipStr)
-	if err != nil {
-		return err
-	}
-	ip := net.ParseIP(ipStr)
-	if ip == nil {
-		return errors.New("Unknown IP format: " + ipStr)
-	}
-	*this = IPAddress(ip)
-	return nil
-}
-
-type SocksConfig struct {
-	AuthMethod string          `json:"auth"`
-	Accounts   SocksAccountMap `json:"accounts"`
-	UDP        bool            `json:"udp"`
-	HostIP     IPAddress       `json:"ip"`
-}
-
-func (sc *SocksConfig) IsNoAuth() bool {
-	return sc.AuthMethod == AuthMethodNoAuth
-}
-
-func (sc *SocksConfig) IsPassword() bool {
-	return sc.AuthMethod == AuthMethodUserPass
-}
-
-func (sc *SocksConfig) HasAccount(user, pass string) bool {
-	return sc.Accounts.HasAccount(user, pass)
-}
-
-func (sc *SocksConfig) IP() net.IP {
-	return net.IP(sc.HostIP)
-}
-
-func (this *SocksConfig) UDPEnabled() bool {
-	return this.UDP
-}
-
-func init() {
-	config.RegisterInboundConnectionConfig("socks", jsonconfig.JsonConfigLoader(func() interface{} {
-		return &SocksConfig{
-			HostIP: IPAddress(net.IPv4(127, 0, 0, 1)),
-		}
-	}))
-}

+ 0 - 65
proxy/socks/json/config_test.go

@@ -1,65 +0,0 @@
-package json
-
-import (
-	"encoding/json"
-	"net"
-	"testing"
-
-	"github.com/v2ray/v2ray-core/proxy/internal/config"
-	v2testing "github.com/v2ray/v2ray-core/testing"
-	"github.com/v2ray/v2ray-core/testing/assert"
-)
-
-func TestAccountMapParsing(t *testing.T) {
-	v2testing.Current(t)
-
-	var accountMap SocksAccountMap
-	err := json.Unmarshal([]byte("[{\"user\": \"a\", \"pass\":\"b\"}, {\"user\": \"c\", \"pass\":\"d\"}]"), &accountMap)
-	assert.Error(err).IsNil()
-
-	assert.Bool(accountMap.HasAccount("a", "b")).IsTrue()
-	assert.Bool(accountMap.HasAccount("a", "c")).IsFalse()
-	assert.Bool(accountMap.HasAccount("c", "d")).IsTrue()
-	assert.Bool(accountMap.HasAccount("e", "d")).IsFalse()
-}
-
-func TestDefaultIPAddress(t *testing.T) {
-	v2testing.Current(t)
-
-	socksConfig, err := config.CreateInboundConnectionConfig("socks", []byte(`{}`))
-	assert.Error(err).IsNil()
-	assert.String(socksConfig.(*SocksConfig).IP()).Equals("127.0.0.1")
-}
-
-func TestIPAddressParsing(t *testing.T) {
-	v2testing.Current(t)
-
-	var ipAddress IPAddress
-	err := json.Unmarshal([]byte("\"1.2.3.4\""), &ipAddress)
-	assert.Error(err).IsNil()
-	assert.String(net.IP(ipAddress)).Equals("1.2.3.4")
-}
-
-func TestNoAuthConfig(t *testing.T) {
-	v2testing.Current(t)
-
-	var config SocksConfig
-	err := json.Unmarshal([]byte("{\"auth\":\"noauth\", \"ip\":\"8.8.8.8\"}"), &config)
-	assert.Error(err).IsNil()
-	assert.Bool(config.IsNoAuth()).IsTrue()
-	assert.Bool(config.IsPassword()).IsFalse()
-	assert.String(config.IP()).Equals("8.8.8.8")
-	assert.Bool(config.UDPEnabled()).IsFalse()
-}
-
-func TestUserPassConfig(t *testing.T) {
-	v2testing.Current(t)
-
-	var config SocksConfig
-	err := json.Unmarshal([]byte("{\"auth\":\"password\", \"accounts\":[{\"user\":\"x\", \"pass\":\"y\"}], \"udp\":true}"), &config)
-	assert.Error(err).IsNil()
-	assert.Bool(config.IsNoAuth()).IsFalse()
-	assert.Bool(config.IsPassword()).IsTrue()
-	assert.Bool(config.HasAccount("x", "y")).IsTrue()
-	assert.Bool(config.UDPEnabled()).IsTrue()
-}

+ 6 - 6
proxy/socks/socks.go

@@ -27,13 +27,13 @@ type SocksServer struct {
 	udpMutex    sync.RWMutex
 	accepting   bool
 	space       app.Space
-	config      Config
+	config      *Config
 	tcpListener *net.TCPListener
 	udpConn     *net.UDPConn
 	udpAddress  v2net.Destination
 }
 
-func NewSocksServer(space app.Space, config Config) *SocksServer {
+func NewSocksServer(space app.Space, config *Config) *SocksServer {
 	return &SocksServer{
 		space:  space,
 		config: config,
@@ -71,7 +71,7 @@ func (this *SocksServer) Listen(port v2net.Port) error {
 	this.tcpListener = listener
 	this.tcpMutex.Unlock()
 	go this.AcceptConnections()
-	if this.config.UDPEnabled() {
+	if this.config.UDPEnabled {
 		this.ListenUDP(port)
 	}
 	return nil
@@ -116,7 +116,7 @@ func (this *SocksServer) HandleConnection(connection *net.TCPConn) error {
 
 func (this *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.Writer, auth protocol.Socks5AuthenticationRequest) error {
 	expectedAuthMethod := protocol.AuthNotRequired
-	if this.config.IsPassword() {
+	if this.config.AuthType == AuthTypePassword {
 		expectedAuthMethod = protocol.AuthUserPass
 	}
 
@@ -137,7 +137,7 @@ func (this *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.Wri
 		log.Error("Socks failed to write authentication: %v", err)
 		return err
 	}
-	if this.config.IsPassword() {
+	if this.config.AuthType == AuthTypePassword {
 		upRequest, err := protocol.ReadUserPassRequest(reader)
 		if err != nil {
 			log.Error("Socks failed to read username and password: %v", err)
@@ -165,7 +165,7 @@ func (this *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.Wri
 		return err
 	}
 
-	if request.Command == protocol.CmdUdpAssociate && this.config.UDPEnabled() {
+	if request.Command == protocol.CmdUdpAssociate && this.config.UDPEnabled {
 		return this.handleUDP(reader, writer)
 	}
 

+ 1 - 2
proxy/socks/socks_test.go

@@ -1,4 +1,4 @@
-package socks
+package socks_test
 
 import (
 	"bytes"
@@ -12,7 +12,6 @@ import (
 	"github.com/v2ray/v2ray-core/app"
 	v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
 	v2proxy "github.com/v2ray/v2ray-core/proxy"
-	_ "github.com/v2ray/v2ray-core/proxy/socks/json"
 	proxytesting "github.com/v2ray/v2ray-core/proxy/testing"
 	proxymocks "github.com/v2ray/v2ray-core/proxy/testing/mocks"
 	"github.com/v2ray/v2ray-core/shell/point"

+ 1 - 1
proxy/socks/socksfactory.go

@@ -9,6 +9,6 @@ import (
 func init() {
 	internal.MustRegisterInboundConnectionHandlerCreator("socks",
 		func(space app.Space, rawConfig interface{}) (proxy.InboundConnectionHandler, error) {
-			return NewSocksServer(space, rawConfig.(Config)), nil
+			return NewSocksServer(space, rawConfig.(*Config)), nil
 		})
 }

+ 1 - 1
proxy/socks/udp.go

@@ -21,7 +21,7 @@ func (this *SocksServer) ListenUDP(port v2net.Port) error {
 		return err
 	}
 	this.udpMutex.Lock()
-	this.udpAddress = v2net.UDPDestination(v2net.IPAddress(this.config.IP()), port)
+	this.udpAddress = v2net.UDPDestination(this.config.Address, port)
 	this.udpConn = conn
 	this.udpMutex.Unlock()
 

+ 2 - 2
proxy/vmess/inbound/config.go

@@ -4,6 +4,6 @@ import (
 	"github.com/v2ray/v2ray-core/proxy/vmess"
 )
 
-type Config interface {
-	AllowedUsers() []vmess.User
+type Config struct {
+	AllowedUsers []*vmess.User
 }

+ 26 - 0
proxy/vmess/inbound/config_json.go

@@ -0,0 +1,26 @@
+// +build json
+
+package inbound
+
+import (
+	"encoding/json"
+
+	"github.com/v2ray/v2ray-core/proxy/internal/config"
+	"github.com/v2ray/v2ray-core/proxy/vmess"
+)
+
+func init() {
+	config.RegisterInboundConnectionConfig("vmess",
+		func(data []byte) (interface{}, error) {
+			type JsonConfig struct {
+				Users []*vmess.User `json:"clients"`
+			}
+			jsonConfig := new(JsonConfig)
+			if err := json.Unmarshal(data, jsonConfig); err != nil {
+				return nil, err
+			}
+			return &Config{
+				AllowedUsers: jsonConfig.Users,
+			}, nil
+		})
+}

+ 3 - 3
proxy/vmess/inbound/inbound.go

@@ -109,7 +109,7 @@ func (this *VMessInboundHandler) HandleConnection(connection *net.TCPConn) error
 	readFinish.Lock()
 	writeFinish.Lock()
 
-	userSettings := vmess.GetUserSettings(request.User.Level())
+	userSettings := vmess.GetUserSettings(request.User.Level)
 	connReader.SetTimeOut(userSettings.PayloadReadTimeout)
 	go handleInput(request, connReader, input, &readFinish)
 
@@ -167,10 +167,10 @@ func handleOutput(request *protocol.VMessRequest, writer io.Writer, output <-cha
 func init() {
 	internal.MustRegisterInboundConnectionHandlerCreator("vmess",
 		func(space app.Space, rawConfig interface{}) (proxy.InboundConnectionHandler, error) {
-			config := rawConfig.(Config)
+			config := rawConfig.(*Config)
 
 			allowedClients := protocol.NewTimedUserSet()
-			for _, user := range config.AllowedUsers() {
+			for _, user := range config.AllowedUsers {
 				allowedClients.AddUser(user)
 			}
 

+ 0 - 26
proxy/vmess/inbound/json/inbound.go

@@ -1,26 +0,0 @@
-package json
-
-import (
-	"github.com/v2ray/v2ray-core/proxy/internal/config"
-	"github.com/v2ray/v2ray-core/proxy/internal/config/json"
-	"github.com/v2ray/v2ray-core/proxy/vmess"
-	vmessjson "github.com/v2ray/v2ray-core/proxy/vmess/json"
-)
-
-type Inbound struct {
-	AllowedClients []*vmessjson.ConfigUser `json:"clients"`
-}
-
-func (c *Inbound) AllowedUsers() []vmess.User {
-	users := make([]vmess.User, 0, len(c.AllowedClients))
-	for _, rawUser := range c.AllowedClients {
-		users = append(users, rawUser)
-	}
-	return users
-}
-
-func init() {
-	config.RegisterInboundConnectionConfig("vmess", json.JsonConfigLoader(func() interface{} {
-		return new(Inbound)
-	}))
-}

+ 0 - 73
proxy/vmess/json/user.go

@@ -1,73 +0,0 @@
-package json
-
-import (
-	"encoding/json"
-	"math/rand"
-
-	"github.com/v2ray/v2ray-core/common/uuid"
-	"github.com/v2ray/v2ray-core/proxy/vmess"
-)
-
-// ConfigUser is an user account in VMess configuration.
-type ConfigUser struct {
-	Id         *vmess.ID
-	Email      string
-	LevelValue vmess.UserLevel
-	AlterIds   []*vmess.ID
-}
-
-func (u *ConfigUser) UnmarshalJSON(data []byte) error {
-	type rawUser struct {
-		IdString     string `json:"id"`
-		EmailString  string `json:"email"`
-		LevelInt     int    `json:"level"`
-		AlterIdCount int    `json:"alterId"`
-	}
-	var rawUserValue rawUser
-	if err := json.Unmarshal(data, &rawUserValue); err != nil {
-		return err
-	}
-	id, err := uuid.ParseString(rawUserValue.IdString)
-	if err != nil {
-		return err
-	}
-	u.Id = vmess.NewID(id)
-	u.Email = rawUserValue.EmailString
-	u.LevelValue = vmess.UserLevel(rawUserValue.LevelInt)
-
-	if rawUserValue.AlterIdCount > 0 {
-		prevId := u.Id.UUID()
-		// TODO: check duplicate
-		u.AlterIds = make([]*vmess.ID, rawUserValue.AlterIdCount)
-		for idx, _ := range u.AlterIds {
-			newid := prevId.Next()
-			u.AlterIds[idx] = vmess.NewID(newid)
-			prevId = newid
-		}
-	}
-
-	return nil
-}
-
-func (u *ConfigUser) ID() *vmess.ID {
-	return u.Id
-}
-
-func (this *ConfigUser) Level() vmess.UserLevel {
-	return this.LevelValue
-}
-
-func (this *ConfigUser) AlterIDs() []*vmess.ID {
-	return this.AlterIds
-}
-
-func (this *ConfigUser) AnyValidID() *vmess.ID {
-	if len(this.AlterIds) == 0 {
-		return this.ID()
-	}
-	if len(this.AlterIds) == 1 {
-		return this.AlterIds[0]
-	}
-	idIdx := rand.Intn(len(this.AlterIds))
-	return this.AlterIds[idIdx]
-}

+ 2 - 2
proxy/vmess/outbound/config.go

@@ -1,5 +1,5 @@
 package outbound
 
-type Config interface {
-	Receivers() []*Receiver
+type Config struct {
+	Receivers []*Receiver
 }

+ 39 - 0
proxy/vmess/outbound/config_json.go

@@ -0,0 +1,39 @@
+// +build json
+
+package outbound
+
+import (
+	"encoding/json"
+
+	"github.com/v2ray/v2ray-core/common/log"
+	"github.com/v2ray/v2ray-core/proxy/internal"
+	proxyconfig "github.com/v2ray/v2ray-core/proxy/internal/config"
+)
+
+func (this *Config) UnmarshalJSON(data []byte) error {
+	type RawOutbound struct {
+		Receivers []*Receiver `json:"vnext"`
+	}
+	rawOutbound := &RawOutbound{}
+	err := json.Unmarshal(data, rawOutbound)
+	if err != nil {
+		return err
+	}
+	if len(rawOutbound.Receivers) == 0 {
+		log.Error("VMess: 0 VMess receiver configured.")
+		return internal.ErrorBadConfiguration
+	}
+	this.Receivers = rawOutbound.Receivers
+	return nil
+}
+
+func init() {
+	proxyconfig.RegisterOutboundConnectionConfig("vmess",
+		func(data []byte) (interface{}, error) {
+			rawConfig := new(Config)
+			if err := json.Unmarshal(data, rawConfig); err != nil {
+				return nil, err
+			}
+			return rawConfig, nil
+		})
+}

+ 0 - 85
proxy/vmess/outbound/json/outbound.go

@@ -1,85 +0,0 @@
-package json
-
-import (
-	"encoding/json"
-
-	"github.com/v2ray/v2ray-core/common/log"
-	v2net "github.com/v2ray/v2ray-core/common/net"
-	v2netjson "github.com/v2ray/v2ray-core/common/net/json"
-	"github.com/v2ray/v2ray-core/proxy/internal"
-	proxyconfig "github.com/v2ray/v2ray-core/proxy/internal/config"
-	jsonconfig "github.com/v2ray/v2ray-core/proxy/internal/config/json"
-	"github.com/v2ray/v2ray-core/proxy/vmess"
-	vmessjson "github.com/v2ray/v2ray-core/proxy/vmess/json"
-	"github.com/v2ray/v2ray-core/proxy/vmess/outbound"
-)
-
-type ConfigTarget struct {
-	Destination v2net.Destination
-	Users       []*vmessjson.ConfigUser
-}
-
-func (t *ConfigTarget) UnmarshalJSON(data []byte) error {
-	type RawConfigTarget struct {
-		Address *v2netjson.Host         `json:"address"`
-		Port    v2net.Port              `json:"port"`
-		Users   []*vmessjson.ConfigUser `json:"users"`
-	}
-	var rawConfig RawConfigTarget
-	if err := json.Unmarshal(data, &rawConfig); err != nil {
-		return err
-	}
-	if len(rawConfig.Users) == 0 {
-		log.Error("0 user configured for VMess outbound.")
-		return internal.ErrorBadConfiguration
-	}
-	t.Users = rawConfig.Users
-	if rawConfig.Address == nil {
-		log.Error("Address is not set in VMess outbound config.")
-		return internal.ErrorBadConfiguration
-	}
-	t.Destination = v2net.TCPDestination(rawConfig.Address.Address(), rawConfig.Port)
-	return nil
-}
-
-type Outbound struct {
-	TargetList []*ConfigTarget `json:"vnext"`
-}
-
-func (this *Outbound) UnmarshalJSON(data []byte) error {
-	type RawOutbound struct {
-		TargetList []*ConfigTarget `json:"vnext"`
-	}
-	rawOutbound := &RawOutbound{}
-	err := json.Unmarshal(data, rawOutbound)
-	if err != nil {
-		return err
-	}
-	if len(rawOutbound.TargetList) == 0 {
-		log.Error("0 VMess receiver configured.")
-		return internal.ErrorBadConfiguration
-	}
-	this.TargetList = rawOutbound.TargetList
-	return nil
-}
-
-func (o *Outbound) Receivers() []*outbound.Receiver {
-	targets := make([]*outbound.Receiver, 0, 2*len(o.TargetList))
-	for _, rawTarget := range o.TargetList {
-		users := make([]vmess.User, 0, len(rawTarget.Users))
-		for _, rawUser := range rawTarget.Users {
-			users = append(users, rawUser)
-		}
-		targets = append(targets, &outbound.Receiver{
-			Destination: rawTarget.Destination,
-			Accounts:    users,
-		})
-	}
-	return targets
-}
-
-func init() {
-	proxyconfig.RegisterOutboundConnectionConfig("vmess", jsonconfig.JsonConfigLoader(func() interface{} {
-		return new(Outbound)
-	}))
-}

+ 2 - 2
proxy/vmess/outbound/outbound.go

@@ -195,10 +195,10 @@ func handleResponse(conn net.Conn, request *protocol.VMessRequest, output chan<-
 func init() {
 	internal.MustRegisterOutboundConnectionHandlerCreator("vmess",
 		func(space app.Space, rawConfig interface{}) (proxy.OutboundConnectionHandler, error) {
-			vOutConfig := rawConfig.(Config)
+			vOutConfig := rawConfig.(*Config)
 			return &VMessOutboundHandler{
 				space:           space,
-				receiverManager: NewReceiverManager(vOutConfig.Receivers()),
+				receiverManager: NewReceiverManager(vOutConfig.Receivers),
 			}, nil
 		})
 }

+ 2 - 2
proxy/vmess/outbound/receiver.go

@@ -9,7 +9,7 @@ import (
 
 type Receiver struct {
 	Destination v2net.Destination
-	Accounts    []vmess.User
+	Accounts    []*vmess.User
 }
 
 type ReceiverManager struct {
@@ -22,7 +22,7 @@ func NewReceiverManager(receivers []*Receiver) *ReceiverManager {
 	}
 }
 
-func (this *ReceiverManager) PickReceiver() (v2net.Destination, vmess.User) {
+func (this *ReceiverManager) PickReceiver() (v2net.Destination, *vmess.User) {
 	receiverLen := len(this.receivers)
 	receiverIdx := 0
 	if receiverLen > 1 {

+ 36 - 0
proxy/vmess/outbound/receiver_json.go

@@ -0,0 +1,36 @@
+// +build json
+
+package outbound
+
+import (
+	"encoding/json"
+
+	"github.com/v2ray/v2ray-core/common/log"
+	v2net "github.com/v2ray/v2ray-core/common/net"
+	v2netjson "github.com/v2ray/v2ray-core/common/net/json"
+	"github.com/v2ray/v2ray-core/proxy/internal"
+	"github.com/v2ray/v2ray-core/proxy/vmess"
+)
+
+func (this *Receiver) UnmarshalJSON(data []byte) error {
+	type RawConfigTarget struct {
+		Address *v2netjson.Host `json:"address"`
+		Port    v2net.Port      `json:"port"`
+		Users   []*vmess.User   `json:"users"`
+	}
+	var rawConfig RawConfigTarget
+	if err := json.Unmarshal(data, &rawConfig); err != nil {
+		return err
+	}
+	if len(rawConfig.Users) == 0 {
+		log.Error("VMess: 0 user configured for VMess outbound.")
+		return internal.ErrorBadConfiguration
+	}
+	this.Accounts = rawConfig.Users
+	if rawConfig.Address == nil {
+		log.Error("VMess: Address is not set in VMess outbound config.")
+		return internal.ErrorBadConfiguration
+	}
+	this.Destination = v2net.TCPDestination(rawConfig.Address.Address(), rawConfig.Port)
+	return nil
+}

+ 9 - 7
proxy/vmess/outbound/json/outbound_test.go → proxy/vmess/outbound/receiver_json_test.go

@@ -1,10 +1,12 @@
-package json_test
+// +build json
+
+package outbound_test
 
 import (
 	"encoding/json"
 	"testing"
 
-	jsonconfig "github.com/v2ray/v2ray-core/proxy/vmess/outbound/json"
+	. "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
 	v2testing "github.com/v2ray/v2ray-core/testing"
 	"github.com/v2ray/v2ray-core/testing/assert"
 )
@@ -24,10 +26,10 @@ func TestConfigTargetParsing(t *testing.T) {
     ]
   }`
 
-	var target *jsonconfig.ConfigTarget
-	err := json.Unmarshal([]byte(rawJson), &target)
+	receiver := new(Receiver)
+	err := json.Unmarshal([]byte(rawJson), &receiver)
 	assert.Error(err).IsNil()
-	assert.String(target.Destination).Equals("tcp:127.0.0.1:80")
-	assert.Int(len(target.Users)).Equals(1)
-	assert.String(target.Users[0].ID()).Equals("e641f5ad-9397-41e3-bf1a-e8740dfed019")
+	assert.String(receiver.Destination).Equals("tcp:127.0.0.1:80")
+	assert.Int(len(receiver.Accounts)).Equals(1)
+	assert.String(receiver.Accounts[0].ID).Equals("e641f5ad-9397-41e3-bf1a-e8740dfed019")
 }

+ 1 - 1
proxy/vmess/protocol/rand_test.go

@@ -4,7 +4,7 @@ import (
 	"testing"
 	"time"
 
-  . "github.com/v2ray/v2ray-core/proxy/vmess/protocol"
+	. "github.com/v2ray/v2ray-core/proxy/vmess/protocol"
 	v2testing "github.com/v2ray/v2ray-core/testing"
 	"github.com/v2ray/v2ray-core/testing/assert"
 )

+ 3 - 3
proxy/vmess/protocol/testing/mockuserset.go

@@ -6,17 +6,17 @@ import (
 )
 
 type MockUserSet struct {
-	Users      []vmess.User
+	Users      []*vmess.User
 	UserHashes map[string]int
 	Timestamps map[string]protocol.Timestamp
 }
 
-func (us *MockUserSet) AddUser(user vmess.User) error {
+func (us *MockUserSet) AddUser(user *vmess.User) error {
 	us.Users = append(us.Users, user)
 	return nil
 }
 
-func (us *MockUserSet) GetUser(userhash []byte) (vmess.User, protocol.Timestamp, bool) {
+func (us *MockUserSet) GetUser(userhash []byte) (*vmess.User, protocol.Timestamp, bool) {
 	idx, found := us.UserHashes[string(userhash)]
 	if found {
 		return us.Users[idx], us.Timestamps[string(userhash)], true

+ 5 - 23
proxy/vmess/protocol/testing/static_userset.go

@@ -6,34 +6,16 @@ import (
 	"github.com/v2ray/v2ray-core/proxy/vmess/protocol"
 )
 
-type StaticUser struct {
-	id *vmess.ID
-}
-
-func (this *StaticUser) ID() *vmess.ID {
-	return this.id
-}
-
-func (this *StaticUser) Level() vmess.UserLevel {
-	return vmess.UserLevelUntrusted
-}
-
-func (this *StaticUser) AlterIDs() []*vmess.ID {
-	return nil
-}
-
-func (this *StaticUser) AnyValidID() *vmess.ID {
-	return this.id
-}
-
 type StaticUserSet struct {
 }
 
-func (us *StaticUserSet) AddUser(user vmess.User) error {
+func (us *StaticUserSet) AddUser(user *vmess.User) error {
 	return nil
 }
 
-func (us *StaticUserSet) GetUser(userhash []byte) (vmess.User, protocol.Timestamp, bool) {
+func (us *StaticUserSet) GetUser(userhash []byte) (*vmess.User, protocol.Timestamp, bool) {
 	id, _ := uuid.ParseString("703e9102-eb57-499c-8b59-faf4f371bb21")
-	return &StaticUser{id: vmess.NewID(id)}, 0, true
+	return &vmess.User{
+		ID: vmess.NewID(id),
+	}, 0, true
 }

+ 8 - 8
proxy/vmess/protocol/userset.go

@@ -38,12 +38,12 @@ type idEntry struct {
 }
 
 type UserSet interface {
-	AddUser(user vmess.User) error
-	GetUser(timeHash []byte) (vmess.User, Timestamp, bool)
+	AddUser(user *vmess.User) error
+	GetUser(timeHash []byte) (*vmess.User, Timestamp, bool)
 }
 
 type TimedUserSet struct {
-	validUsers []vmess.User
+	validUsers []*vmess.User
 	userHash   map[[16]byte]indexTimePair
 	ids        []*idEntry
 	access     sync.RWMutex
@@ -56,7 +56,7 @@ type indexTimePair struct {
 
 func NewTimedUserSet() UserSet {
 	tus := &TimedUserSet{
-		validUsers: make([]vmess.User, 0, 16),
+		validUsers: make([]*vmess.User, 0, 16),
 		userHash:   make(map[[16]byte]indexTimePair, 512),
 		access:     sync.RWMutex{},
 		ids:        make([]*idEntry, 0, 512),
@@ -94,21 +94,21 @@ func (us *TimedUserSet) updateUserHash(tick <-chan time.Time) {
 	}
 }
 
-func (us *TimedUserSet) AddUser(user vmess.User) error {
+func (us *TimedUserSet) AddUser(user *vmess.User) error {
 	idx := len(us.validUsers)
 	us.validUsers = append(us.validUsers, user)
 
 	nowSec := time.Now().Unix()
 
 	entry := &idEntry{
-		id:      user.ID(),
+		id:      user.ID,
 		userIdx: idx,
 		lastSec: Timestamp(nowSec - cacheDurationSec),
 		hashes:  collect.NewSizedQueue(2*cacheDurationSec + 1),
 	}
 	us.generateNewHashes(Timestamp(nowSec+cacheDurationSec), idx, entry)
 	us.ids = append(us.ids, entry)
-	for _, alterid := range user.AlterIDs() {
+	for _, alterid := range user.AlterIDs {
 		entry := &idEntry{
 			id:      alterid,
 			userIdx: idx,
@@ -122,7 +122,7 @@ func (us *TimedUserSet) AddUser(user vmess.User) error {
 	return nil
 }
 
-func (us *TimedUserSet) GetUser(userHash []byte) (vmess.User, Timestamp, bool) {
+func (us *TimedUserSet) GetUser(userHash []byte) (*vmess.User, Timestamp, bool) {
 	defer us.access.RUnlock()
 	us.access.RLock()
 	var fixedSizeHash [16]byte

+ 3 - 3
proxy/vmess/protocol/vmess.go

@@ -34,7 +34,7 @@ const (
 // streaming.
 type VMessRequest struct {
 	Version        byte
-	User           vmess.User
+	User           *vmess.User
 	RequestIV      []byte
 	RequestKey     []byte
 	ResponseHeader []byte
@@ -83,7 +83,7 @@ func (this *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
 	timestampHash := TimestampHash()
 	timestampHash.Write(timeSec.HashBytes())
 	iv := timestampHash.Sum(nil)
-	aesStream, err := v2crypto.NewAesDecryptionStream(userObj.ID().CmdKey(), iv)
+	aesStream, err := v2crypto.NewAesDecryptionStream(userObj.ID.CmdKey(), iv)
 	if err != nil {
 		log.Debug("VMess: Failed to create AES stream: %v", err)
 		return nil, err
@@ -217,7 +217,7 @@ func (this *VMessRequest) ToBytes(timestampGenerator RandomTimestampGenerator, b
 	timestampHash := md5.New()
 	timestampHash.Write(timestamp.HashBytes())
 	iv := timestampHash.Sum(nil)
-	aesStream, err := v2crypto.NewAesEncryptionStream(this.User.ID().CmdKey(), iv)
+	aesStream, err := v2crypto.NewAesEncryptionStream(this.User.ID.CmdKey(), iv)
 	if err != nil {
 		return nil, err
 	}

+ 7 - 28
proxy/vmess/protocol/vmess_test.go

@@ -24,27 +24,6 @@ func (this *FakeTimestampGenerator) Next() Timestamp {
 	return this.timestamp
 }
 
-type TestUser struct {
-	id    *vmess.ID
-	level vmess.UserLevel
-}
-
-func (u *TestUser) ID() *vmess.ID {
-	return u.id
-}
-
-func (this *TestUser) Level() vmess.UserLevel {
-	return this.level
-}
-
-func (this *TestUser) AlterIDs() []*vmess.ID {
-	return nil
-}
-
-func (this *TestUser) AnyValidID() *vmess.ID {
-	return this.id
-}
-
 func TestVMessSerialization(t *testing.T) {
 	v2testing.Current(t)
 
@@ -53,11 +32,11 @@ func TestVMessSerialization(t *testing.T) {
 
 	userId := vmess.NewID(id)
 
-	testUser := &TestUser{
-		id: userId,
+	testUser := &vmess.User{
+		ID: userId,
 	}
 
-	userSet := protocoltesting.MockUserSet{[]vmess.User{}, make(map[string]int), make(map[string]Timestamp)}
+	userSet := protocoltesting.MockUserSet{[]*vmess.User{}, make(map[string]int), make(map[string]Timestamp)}
 	userSet.AddUser(testUser)
 
 	request := new(VMessRequest)
@@ -92,7 +71,7 @@ func TestVMessSerialization(t *testing.T) {
 	}
 
 	assert.Byte(actualRequest.Version).Named("Version").Equals(byte(0x01))
-	assert.String(actualRequest.User.ID()).Named("UserId").Equals(request.User.ID().String())
+	assert.String(actualRequest.User.ID).Named("UserId").Equals(request.User.ID.String())
 	assert.Bytes(actualRequest.RequestIV).Named("RequestIV").Equals(request.RequestIV[:])
 	assert.Bytes(actualRequest.RequestKey).Named("RequestKey").Equals(request.RequestKey[:])
 	assert.Bytes(actualRequest.ResponseHeader).Named("ResponseHeader").Equals(request.ResponseHeader[:])
@@ -113,10 +92,10 @@ func BenchmarkVMessRequestWriting(b *testing.B) {
 	assert.Error(err).IsNil()
 
 	userId := vmess.NewID(id)
-	userSet := protocoltesting.MockUserSet{[]vmess.User{}, make(map[string]int), make(map[string]Timestamp)}
+	userSet := protocoltesting.MockUserSet{[]*vmess.User{}, make(map[string]int), make(map[string]Timestamp)}
 
-	testUser := &TestUser{
-		id: userId,
+	testUser := &vmess.User{
+		ID: userId,
 	}
 	userSet.AddUser(testUser)
 

+ 19 - 5
proxy/vmess/user.go

@@ -1,5 +1,9 @@
 package vmess
 
+import (
+	"math/rand"
+)
+
 type UserLevel int
 
 const (
@@ -7,11 +11,21 @@ const (
 	UserLevelUntrusted = UserLevel(0)
 )
 
-type User interface {
-	ID() *ID
-	AlterIDs() []*ID
-	Level() UserLevel
-	AnyValidID() *ID
+type User struct {
+	ID       *ID
+	AlterIDs []*ID
+	Level    UserLevel
+}
+
+func (this *User) AnyValidID() *ID {
+	if len(this.AlterIDs) == 0 {
+		return this.ID
+	}
+	if len(this.AlterIDs) == 1 {
+		return this.AlterIDs[0]
+	}
+	idx := rand.Intn(len(this.AlterIDs))
+	return this.AlterIDs[idx]
 }
 
 type UserSettings struct {

+ 42 - 0
proxy/vmess/user_json.go

@@ -0,0 +1,42 @@
+// +build json
+
+package vmess
+
+import (
+	"encoding/json"
+
+	"github.com/v2ray/v2ray-core/common/uuid"
+)
+
+func (u *User) UnmarshalJSON(data []byte) error {
+	type rawUser struct {
+		IdString     string `json:"id"`
+		EmailString  string `json:"email"`
+		LevelInt     int    `json:"level"`
+		AlterIdCount int    `json:"alterId"`
+	}
+	var rawUserValue rawUser
+	if err := json.Unmarshal(data, &rawUserValue); err != nil {
+		return err
+	}
+	id, err := uuid.ParseString(rawUserValue.IdString)
+	if err != nil {
+		return err
+	}
+	u.ID = NewID(id)
+	//u.Email = rawUserValue.EmailString
+	u.Level = UserLevel(rawUserValue.LevelInt)
+
+	if rawUserValue.AlterIdCount > 0 {
+		prevId := u.ID.UUID()
+		// TODO: check duplicate
+		u.AlterIDs = make([]*ID, rawUserValue.AlterIdCount)
+		for idx, _ := range u.AlterIDs {
+			newid := prevId.Next()
+			u.AlterIDs[idx] = NewID(newid)
+			prevId = newid
+		}
+	}
+
+	return nil
+}

+ 0 - 2
proxy/vmess/vmess_test.go

@@ -13,9 +13,7 @@ import (
 	proxymocks "github.com/v2ray/v2ray-core/proxy/testing/mocks"
 	vmess "github.com/v2ray/v2ray-core/proxy/vmess"
 	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound"
-	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound/json"
 	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
-	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound/json"
 	"github.com/v2ray/v2ray-core/shell/point"
 	"github.com/v2ray/v2ray-core/shell/point/testing/mocks"
 	v2testing "github.com/v2ray/v2ray-core/testing"

+ 0 - 7
release/server/main.go

@@ -16,19 +16,12 @@ import (
 
 	// The following are neccesary as they register handlers in their init functions.
 	_ "github.com/v2ray/v2ray-core/proxy/blackhole"
-	_ "github.com/v2ray/v2ray-core/proxy/blackhole/json"
 	_ "github.com/v2ray/v2ray-core/proxy/dokodemo"
-	_ "github.com/v2ray/v2ray-core/proxy/dokodemo/json"
 	_ "github.com/v2ray/v2ray-core/proxy/freedom"
-	_ "github.com/v2ray/v2ray-core/proxy/freedom/json"
 	_ "github.com/v2ray/v2ray-core/proxy/http"
-	_ "github.com/v2ray/v2ray-core/proxy/http/json"
 	_ "github.com/v2ray/v2ray-core/proxy/socks"
-	_ "github.com/v2ray/v2ray-core/proxy/socks/json"
 	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound"
-	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound/json"
 	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
-	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound/json"
 )
 
 var (

+ 5 - 5
shell/point/json/json_test.go

@@ -5,11 +5,11 @@ import (
 	"testing"
 
 	netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
-	_ "github.com/v2ray/v2ray-core/proxy/dokodemo/json"
-	_ "github.com/v2ray/v2ray-core/proxy/freedom/json"
-	_ "github.com/v2ray/v2ray-core/proxy/socks/json"
-	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound/json"
-	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound/json"
+	_ "github.com/v2ray/v2ray-core/proxy/dokodemo"
+	_ "github.com/v2ray/v2ray-core/proxy/freedom"
+	_ "github.com/v2ray/v2ray-core/proxy/socks"
+	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound"
+	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
 	"github.com/v2ray/v2ray-core/shell/point/json"
 
 	v2testing "github.com/v2ray/v2ray-core/testing"

+ 0 - 6
testing/scenarios/server_env.go

@@ -13,17 +13,11 @@ import (
 
 	// The following are neccesary as they register handlers in their init functions.
 	_ "github.com/v2ray/v2ray-core/proxy/blackhole"
-	_ "github.com/v2ray/v2ray-core/proxy/blackhole/json"
 	_ "github.com/v2ray/v2ray-core/proxy/dokodemo"
-	_ "github.com/v2ray/v2ray-core/proxy/dokodemo/json"
 	_ "github.com/v2ray/v2ray-core/proxy/freedom"
-	_ "github.com/v2ray/v2ray-core/proxy/freedom/json"
 	_ "github.com/v2ray/v2ray-core/proxy/socks"
-	_ "github.com/v2ray/v2ray-core/proxy/socks/json"
 	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound"
-	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound/json"
 	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
-	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound/json"
 )
 
 var (

+ 1 - 1
tools/build/go.go

@@ -14,7 +14,7 @@ func buildV2Ray(targetFile string, version string, goOS GoOS, goArch GoArch) err
 		today := fmt.Sprintf("%04d%02d%02d", year, int(month), day)
 		ldFlags = ldFlags + " -X github.com/v2ray/v2ray-core.version=" + version + " -X github.com/v2ray/v2ray-core.build=" + today
 	}
-	cmd := exec.Command("go", "build", "-o", targetFile, "-compiler", "gc", "-ldflags", ldFlags, "github.com/v2ray/v2ray-core/release/server")
+	cmd := exec.Command("go", "build", "-tags", "json", "-o", targetFile, "-compiler", "gc", "-ldflags", ldFlags, "github.com/v2ray/v2ray-core/release/server")
 	cmd.Env = append(cmd.Env, "GOOS="+string(goOS), "GOARCH="+string(goArch))
 	cmd.Env = append(cmd.Env, os.Environ()...)
 	output, err := cmd.CombinedOutput()