Bläddra i källkod

protobuf for stream settings

Darien Raymond 9 år sedan
förälder
incheckning
1d13f47f9c
59 ändrade filer med 1243 tillägg och 323 borttagningar
  1. 2 2
      app/dns/server_test.go
  2. 5 1
      common/net/network.go
  3. 9 0
      common/net/network_json.go
  4. 9 0
      common/net/network_json_test.go
  5. 4 3
      proxy/blackhole/blackhole.go
  6. 4 2
      proxy/dokodemo/dokodemo.go
  7. 8 8
      proxy/dokodemo/dokodemo_test.go
  8. 4 2
      proxy/freedom/freedom.go
  9. 6 6
      proxy/freedom/freedom_test.go
  10. 4 2
      proxy/http/server.go
  11. 2 2
      proxy/http/server_test.go
  12. 2 2
      proxy/proxy.go
  13. 3 3
      proxy/registry/creator.go
  14. 12 6
      proxy/registry/handler_cache.go
  15. 4 2
      proxy/shadowsocks/server.go
  16. 4 2
      proxy/socks/server.go
  17. 4 2
      proxy/vmess/inbound/inbound.go
  18. 4 2
      proxy/vmess/outbound/outbound.go
  19. 4 4
      shell/point/config.go
  20. 16 16
      shell/point/config_json.go
  21. 2 17
      transport/config.go
  22. 67 0
      transport/config.pb.go
  23. 3 1
      transport/config.proto
  24. 38 4
      transport/config_json.go
  25. 4 0
      transport/internet/authenticator.pb.go
  26. 108 0
      transport/internet/config.go
  27. 132 0
      transport/internet/config.pb.go
  28. 31 0
      transport/internet/config.proto
  29. 0 51
      transport/internet/connection.go
  30. 20 43
      transport/internet/connection_json.go
  31. 7 7
      transport/internet/dialer.go
  32. 8 7
      transport/internet/kcp/config.go
  33. 8 5
      transport/internet/kcp/connection.go
  34. 3 3
      transport/internet/kcp/connection_test.go
  35. 16 4
      transport/internet/kcp/dialer.go
  36. 28 2
      transport/internet/kcp/kcp_test.go
  37. 19 5
      transport/internet/kcp/listener.go
  38. 2 1
      transport/internet/kcp/output.go
  39. 2 2
      transport/internet/kcp/receiving.go
  40. 7 7
      transport/internet/kcp/sending.go
  41. 10 11
      transport/internet/tcp/config.go
  42. 58 0
      transport/internet/tcp/config.pb.go
  43. 10 0
      transport/internet/tcp/config.proto
  44. 5 3
      transport/internet/tcp/connection.go
  45. 16 4
      transport/internet/tcp/dialer.go
  46. 18 3
      transport/internet/tcp/hub.go
  47. 8 8
      transport/internet/tcp_hub.go
  48. 39 0
      transport/internet/tls/config.go
  49. 82 0
      transport/internet/tls/config.pb.go
  50. 16 0
      transport/internet/tls/config.proto
  51. 41 0
      transport/internet/tls/config_json.go
  52. 10 13
      transport/internet/ws/config.go
  53. 60 0
      transport/internet/ws/config.pb.go
  54. 11 0
      transport/internet/ws/config.proto
  55. 5 3
      transport/internet/ws/connection.go
  56. 28 6
      transport/internet/ws/dialer.go
  57. 18 6
      transport/internet/ws/hub.go
  58. 191 39
      transport/internet/ws/ws_test.go
  59. 2 1
      transport/internet/ws/wsconn.go

+ 2 - 2
app/dns/server_test.go

@@ -28,8 +28,8 @@ func TestDnsAdd(t *testing.T) {
 			space,
 			&proxy.OutboundHandlerMeta{
 				Address: v2net.AnyIP,
-				StreamSettings: &internet.StreamSettings{
-					Type: internet.StreamConnectionTypeRawTCP,
+				StreamSettings: &internet.StreamConfig{
+					Network: v2net.Network_RawTCP,
 				},
 			}))
 	space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager)

+ 5 - 1
common/net/network.go

@@ -68,7 +68,7 @@ func NewNetworkList(networks collect.StringList) *NetworkList {
 }
 
 // HashNetwork returns true if the given network is in this NetworkList.
-func (this *NetworkList) HasNetwork(network Network) bool {
+func (this NetworkList) HasNetwork(network Network) bool {
 	for _, value := range this.Network {
 		if string(value) == string(network) {
 			return true
@@ -76,3 +76,7 @@ func (this *NetworkList) HasNetwork(network Network) bool {
 	}
 	return false
 }
+
+func (this NetworkList) Get(idx int) Network {
+	return this.Network[idx]
+}

+ 9 - 0
common/net/network_json.go

@@ -8,6 +8,15 @@ import (
 	"v2ray.com/core/common/collect"
 )
 
+func (this *Network) UnmarshalJSON(data []byte) error {
+	var str string
+	if err := json.Unmarshal(data, &str); err != nil {
+		return err
+	}
+	*this = ParseNetwork(str)
+	return nil
+}
+
 func (this *NetworkList) UnmarshalJSON(data []byte) error {
 	var strlist collect.StringList
 	if err := json.Unmarshal(data, &strlist); err != nil {

+ 9 - 0
common/net/network_json_test.go

@@ -10,6 +10,15 @@ import (
 	"v2ray.com/core/testing/assert"
 )
 
+func TestStringNetwork(t *testing.T) {
+	assert := assert.On(t)
+
+	var network Network
+	err := json.Unmarshal([]byte(`"tcp"`), &network)
+	assert.Error(err).IsNil()
+	assert.Bool(network == Network_TCP).IsTrue()
+}
+
 func TestArrayNetworkList(t *testing.T) {
 	assert := assert.On(t)
 

+ 4 - 3
proxy/blackhole/blackhole.go

@@ -6,7 +6,6 @@ import (
 	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/proxy"
 	"v2ray.com/core/proxy/registry"
-	"v2ray.com/core/transport/internet"
 	"v2ray.com/core/transport/ray"
 )
 
@@ -40,8 +39,10 @@ func (this *BlackHole) Dispatch(destination v2net.Destination, payload *alloc.Bu
 
 type Factory struct{}
 
-func (this *Factory) StreamCapability() internet.StreamConnectionType {
-	return internet.StreamConnectionTypeRawTCP
+func (this *Factory) StreamCapability() v2net.NetworkList {
+	return v2net.NetworkList{
+		Network: []v2net.Network{v2net.Network_RawTCP},
+	}
 }
 
 func (this *Factory) Create(space app.Space, config interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {

+ 4 - 2
proxy/dokodemo/dokodemo.go

@@ -194,8 +194,10 @@ func (this *DokodemoDoor) HandleTCPConnection(conn internet.Connection) {
 
 type Factory struct{}
 
-func (this *Factory) StreamCapability() internet.StreamConnectionType {
-	return internet.StreamConnectionTypeRawTCP
+func (this *Factory) StreamCapability() v2net.NetworkList {
+	return v2net.NetworkList{
+		Network: []v2net.Network{v2net.Network_RawTCP},
+	}
 }
 
 func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {

+ 8 - 8
proxy/dokodemo/dokodemo_test.go

@@ -44,8 +44,8 @@ func TestDokodemoTCP(t *testing.T) {
 			space,
 			&proxy.OutboundHandlerMeta{
 				Address: v2net.LocalHostIP,
-				StreamSettings: &internet.StreamSettings{
-					Type: internet.StreamConnectionTypeRawTCP,
+				StreamSettings: &internet.StreamConfig{
+					Network: v2net.Network_RawTCP,
 				},
 			}))
 	space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, ohm)
@@ -65,8 +65,8 @@ func TestDokodemoTCP(t *testing.T) {
 	}, space, &proxy.InboundHandlerMeta{
 		Address: v2net.LocalHostIP,
 		Port:    port,
-		StreamSettings: &internet.StreamSettings{
-			Type: internet.StreamConnectionTypeRawTCP,
+		StreamSettings: &internet.StreamConfig{
+			Network: v2net.Network_RawTCP,
 		}})
 	defer dokodemo.Close()
 
@@ -119,8 +119,8 @@ func TestDokodemoUDP(t *testing.T) {
 			space,
 			&proxy.OutboundHandlerMeta{
 				Address: v2net.AnyIP,
-				StreamSettings: &internet.StreamSettings{
-					Type: internet.StreamConnectionTypeRawTCP,
+				StreamSettings: &internet.StreamConfig{
+					Network: v2net.Network_RawTCP,
 				}}))
 	space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, ohm)
 
@@ -139,8 +139,8 @@ func TestDokodemoUDP(t *testing.T) {
 	}, space, &proxy.InboundHandlerMeta{
 		Address: v2net.LocalHostIP,
 		Port:    port,
-		StreamSettings: &internet.StreamSettings{
-			Type: internet.StreamConnectionTypeRawTCP,
+		StreamSettings: &internet.StreamConfig{
+			Network: v2net.Network_RawTCP,
 		}})
 	defer dokodemo.Close()
 

+ 4 - 2
proxy/freedom/freedom.go

@@ -129,8 +129,10 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *
 
 type FreedomFactory struct{}
 
-func (this *FreedomFactory) StreamCapability() internet.StreamConnectionType {
-	return internet.StreamConnectionTypeRawTCP
+func (this *FreedomFactory) StreamCapability() v2net.NetworkList {
+	return v2net.NetworkList{
+		Network: []v2net.Network{v2net.Network_RawTCP},
+	}
 }
 
 func (this *FreedomFactory) Create(space app.Space, config interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {

+ 6 - 6
proxy/freedom/freedom_test.go

@@ -40,8 +40,8 @@ func TestSinglePacket(t *testing.T) {
 		space,
 		&proxy.OutboundHandlerMeta{
 			Address: v2net.AnyIP,
-			StreamSettings: &internet.StreamSettings{
-				Type: internet.StreamConnectionTypeRawTCP,
+			StreamSettings: &internet.StreamConfig{
+				Network: v2net.Network_RawTCP,
 			},
 		})
 	space.Initialize()
@@ -68,8 +68,8 @@ func TestUnreachableDestination(t *testing.T) {
 		app.NewSpace(),
 		&proxy.OutboundHandlerMeta{
 			Address: v2net.AnyIP,
-			StreamSettings: &internet.StreamSettings{
-				Type: internet.StreamConnectionTypeRawTCP,
+			StreamSettings: &internet.StreamConfig{
+				Network: v2net.Network_RawTCP,
 			},
 		})
 	traffic := ray.NewRay()
@@ -104,8 +104,8 @@ func TestIPResolution(t *testing.T) {
 		space,
 		&proxy.OutboundHandlerMeta{
 			Address: v2net.AnyIP,
-			StreamSettings: &internet.StreamSettings{
-				Type: internet.StreamConnectionTypeRawTCP,
+			StreamSettings: &internet.StreamConfig{
+				Network: v2net.Network_RawTCP,
 			},
 		})
 

+ 4 - 2
proxy/http/server.go

@@ -264,8 +264,10 @@ func (this *Server) handlePlainHTTP(request *http.Request, session *proxy.Sessio
 
 type ServerFactory struct{}
 
-func (this *ServerFactory) StreamCapability() internet.StreamConnectionType {
-	return internet.StreamConnectionTypeRawTCP
+func (this *ServerFactory) StreamCapability() v2net.NetworkList {
+	return v2net.NetworkList{
+		Network: []v2net.Network{v2net.Network_RawTCP},
+	}
 }
 
 func (this *ServerFactory) Create(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {

+ 2 - 2
proxy/http/server_test.go

@@ -63,8 +63,8 @@ func TestNormalGetRequest(t *testing.T) {
 		&proxy.InboundHandlerMeta{
 			Address: v2net.LocalHostIP,
 			Port:    port,
-			StreamSettings: &internet.StreamSettings{
-				Type: internet.StreamConnectionTypeRawTCP,
+			StreamSettings: &internet.StreamConfig{
+				Network: v2net.Network_RawTCP,
 			}})
 	defer httpProxy.Close()
 

+ 2 - 2
proxy/proxy.go

@@ -27,13 +27,13 @@ type InboundHandlerMeta struct {
 	Address                v2net.Address
 	Port                   v2net.Port
 	AllowPassiveConnection bool
-	StreamSettings         *internet.StreamSettings
+	StreamSettings         *internet.StreamConfig
 }
 
 type OutboundHandlerMeta struct {
 	Tag            string
 	Address        v2net.Address
-	StreamSettings *internet.StreamSettings
+	StreamSettings *internet.StreamConfig
 }
 
 // An InboundHandler handles inbound network connections to V2Ray.

+ 3 - 3
proxy/registry/creator.go

@@ -2,16 +2,16 @@ package registry
 
 import (
 	"v2ray.com/core/app"
+	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/proxy"
-	"v2ray.com/core/transport/internet"
 )
 
 type InboundHandlerFactory interface {
-	StreamCapability() internet.StreamConnectionType
+	StreamCapability() v2net.NetworkList
 	Create(space app.Space, config interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error)
 }
 
 type OutboundHandlerFactory interface {
-	StreamCapability() internet.StreamConnectionType
+	StreamCapability() v2net.NetworkList
 	Create(space app.Space, config interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error)
 }

+ 12 - 6
proxy/registry/handler_cache.go

@@ -1,6 +1,8 @@
 package registry
 
 import (
+	"errors"
+
 	"v2ray.com/core/app"
 	"v2ray.com/core/common"
 	"v2ray.com/core/proxy"
@@ -46,11 +48,13 @@ func CreateInboundHandler(name string, space app.Space, rawConfig []byte, meta *
 		return nil, common.ErrObjectNotFound
 	}
 	if meta.StreamSettings == nil {
-		meta.StreamSettings = &internet.StreamSettings{
-			Type: creator.StreamCapability(),
+		meta.StreamSettings = &internet.StreamConfig{
+			Network: creator.StreamCapability().Get(0),
 		}
 	} else {
-		meta.StreamSettings.Type &= creator.StreamCapability()
+		if !creator.StreamCapability().HasNetwork(meta.StreamSettings.Network) {
+			return nil, errors.New("Proxy|Registry: Invalid network: " + meta.StreamSettings.Network.String())
+		}
 	}
 
 	if len(rawConfig) > 0 {
@@ -69,11 +73,13 @@ func CreateOutboundHandler(name string, space app.Space, rawConfig []byte, meta
 		return nil, common.ErrObjectNotFound
 	}
 	if meta.StreamSettings == nil {
-		meta.StreamSettings = &internet.StreamSettings{
-			Type: creator.StreamCapability(),
+		meta.StreamSettings = &internet.StreamConfig{
+			Network: creator.StreamCapability().Get(0),
 		}
 	} else {
-		meta.StreamSettings.Type &= creator.StreamCapability()
+		if !creator.StreamCapability().HasNetwork(meta.StreamSettings.Network) {
+			return nil, errors.New("Proxy|Registry: Invalid network: " + meta.StreamSettings.Network.String())
+		}
 	}
 
 	if len(rawConfig) > 0 {

+ 4 - 2
proxy/shadowsocks/server.go

@@ -278,8 +278,10 @@ func (this *Server) handleConnection(conn internet.Connection) {
 
 type ServerFactory struct{}
 
-func (this *ServerFactory) StreamCapability() internet.StreamConnectionType {
-	return internet.StreamConnectionTypeRawTCP
+func (this *ServerFactory) StreamCapability() v2net.NetworkList {
+	return v2net.NetworkList{
+		Network: []v2net.Network{v2net.Network_RawTCP},
+	}
 }
 
 func (this *ServerFactory) Create(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {

+ 4 - 2
proxy/socks/server.go

@@ -315,8 +315,10 @@ func (this *Server) transport(reader io.Reader, writer io.Writer, session *proxy
 
 type ServerFactory struct{}
 
-func (this *ServerFactory) StreamCapability() internet.StreamConnectionType {
-	return internet.StreamConnectionTypeRawTCP
+func (this *ServerFactory) StreamCapability() v2net.NetworkList {
+	return v2net.NetworkList{
+		Network: []v2net.Network{v2net.Network_RawTCP},
+	}
 }
 
 func (this *ServerFactory) Create(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {

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

@@ -249,8 +249,10 @@ func (this *VMessInboundHandler) HandleConnection(connection internet.Connection
 
 type Factory struct{}
 
-func (this *Factory) StreamCapability() internet.StreamConnectionType {
-	return internet.StreamConnectionTypeRawTCP | internet.StreamConnectionTypeTCP | internet.StreamConnectionTypeKCP | internet.StreamConnectionTypeWebSocket
+func (this *Factory) StreamCapability() v2net.NetworkList {
+	return v2net.NetworkList{
+		Network: []v2net.Network{v2net.Network_TCP, v2net.Network_KCP, v2net.Network_WebSocket},
+	}
 }
 
 func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {

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

@@ -160,8 +160,10 @@ func (this *VMessOutboundHandler) handleResponse(session *encoding.ClientSession
 
 type Factory struct{}
 
-func (this *Factory) StreamCapability() internet.StreamConnectionType {
-	return internet.StreamConnectionTypeRawTCP | internet.StreamConnectionTypeTCP | internet.StreamConnectionTypeKCP | internet.StreamConnectionTypeWebSocket
+func (this *Factory) StreamCapability() v2net.NetworkList {
+	return v2net.NetworkList{
+		Network: []v2net.Network{v2net.Network_TCP, v2net.Network_KCP, v2net.Network_WebSocket},
+	}
 }
 
 func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {

+ 4 - 4
shell/point/config.go

@@ -13,7 +13,7 @@ import (
 type InboundConnectionConfig struct {
 	Port                   v2net.Port
 	ListenOn               v2net.Address
-	StreamSettings         *internet.StreamSettings
+	StreamSettings         *internet.StreamConfig
 	Protocol               string
 	Settings               []byte
 	AllowPassiveConnection bool
@@ -22,7 +22,7 @@ type InboundConnectionConfig struct {
 type OutboundConnectionConfig struct {
 	Protocol       string
 	SendThrough    v2net.Address
-	StreamSettings *internet.StreamSettings
+	StreamSettings *internet.StreamConfig
 	Settings       []byte
 }
 
@@ -50,7 +50,7 @@ type InboundDetourConfig struct {
 	ListenOn               v2net.Address
 	Tag                    string
 	Allocation             *InboundDetourAllocationConfig
-	StreamSettings         *internet.StreamSettings
+	StreamSettings         *internet.StreamConfig
 	Settings               []byte
 	AllowPassiveConnection bool
 }
@@ -58,7 +58,7 @@ type InboundDetourConfig struct {
 type OutboundDetourConfig struct {
 	Protocol       string
 	SendThrough    v2net.Address
-	StreamSettings *internet.StreamSettings
+	StreamSettings *internet.StreamConfig
 	Tag            string
 	Settings       []byte
 }

+ 16 - 16
shell/point/config_json.go

@@ -73,12 +73,12 @@ func (this *Config) UnmarshalJSON(data []byte) error {
 
 func (this *InboundConnectionConfig) UnmarshalJSON(data []byte) error {
 	type JsonConfig struct {
-		Port          uint16                   `json:"port"`
-		Listen        *v2net.AddressPB         `json:"listen"`
-		Protocol      string                   `json:"protocol"`
-		StreamSetting *internet.StreamSettings `json:"streamSettings"`
-		Settings      json.RawMessage          `json:"settings"`
-		AllowPassive  bool                     `json:"allowPassive"`
+		Port          uint16                 `json:"port"`
+		Listen        *v2net.AddressPB       `json:"listen"`
+		Protocol      string                 `json:"protocol"`
+		StreamSetting *internet.StreamConfig `json:"streamSettings"`
+		Settings      json.RawMessage        `json:"settings"`
+		AllowPassive  bool                   `json:"allowPassive"`
 	}
 
 	jsonConfig := new(JsonConfig)
@@ -105,10 +105,10 @@ func (this *InboundConnectionConfig) UnmarshalJSON(data []byte) error {
 
 func (this *OutboundConnectionConfig) UnmarshalJSON(data []byte) error {
 	type JsonConnectionConfig struct {
-		Protocol      string                   `json:"protocol"`
-		SendThrough   *v2net.AddressPB         `json:"sendThrough"`
-		StreamSetting *internet.StreamSettings `json:"streamSettings"`
-		Settings      json.RawMessage          `json:"settings"`
+		Protocol      string                 `json:"protocol"`
+		SendThrough   *v2net.AddressPB       `json:"sendThrough"`
+		StreamSetting *internet.StreamConfig `json:"streamSettings"`
+		Settings      json.RawMessage        `json:"settings"`
 	}
 	jsonConfig := new(JsonConnectionConfig)
 	if err := json.Unmarshal(data, jsonConfig); err != nil {
@@ -194,7 +194,7 @@ func (this *InboundDetourConfig) UnmarshalJSON(data []byte) error {
 		Settings      json.RawMessage                `json:"settings"`
 		Tag           string                         `json:"tag"`
 		Allocation    *InboundDetourAllocationConfig `json:"allocate"`
-		StreamSetting *internet.StreamSettings       `json:"streamSettings"`
+		StreamSetting *internet.StreamConfig         `json:"streamSettings"`
 		AllowPassive  bool                           `json:"allowPassive"`
 	}
 	jsonConfig := new(JsonInboundDetourConfig)
@@ -232,11 +232,11 @@ func (this *InboundDetourConfig) UnmarshalJSON(data []byte) error {
 
 func (this *OutboundDetourConfig) UnmarshalJSON(data []byte) error {
 	type JsonOutboundDetourConfig struct {
-		Protocol      string                   `json:"protocol"`
-		SendThrough   *v2net.AddressPB         `json:"sendThrough"`
-		Tag           string                   `json:"tag"`
-		Settings      json.RawMessage          `json:"settings"`
-		StreamSetting *internet.StreamSettings `json:"streamSettings"`
+		Protocol      string                 `json:"protocol"`
+		SendThrough   *v2net.AddressPB       `json:"sendThrough"`
+		Tag           string                 `json:"tag"`
+		Settings      json.RawMessage        `json:"settings"`
+		StreamSetting *internet.StreamConfig `json:"streamSettings"`
 	}
 	jsonConfig := new(JsonOutboundDetourConfig)
 	if err := json.Unmarshal(data, jsonConfig); err != nil {

+ 2 - 17
transport/config.go

@@ -1,26 +1,11 @@
 package transport
 
 import (
-	"v2ray.com/core/transport/internet/kcp"
-	"v2ray.com/core/transport/internet/tcp"
-	"v2ray.com/core/transport/internet/ws"
+	"v2ray.com/core/transport/internet"
 )
 
-// Config for V2Ray transport layer.
-type Config struct {
-	tcpConfig *tcp.Config
-	kcpConfig kcp.Config
-	wsConfig  *ws.Config
-}
-
 // Apply applies this Config.
 func (this *Config) Apply() error {
-	if this.tcpConfig != nil {
-		this.tcpConfig.Apply()
-	}
-	this.kcpConfig.Apply()
-	if this.wsConfig != nil {
-		this.wsConfig.Apply()
-	}
+	internet.ApplyGlobalNetworkSettings(this.NetworkSettings)
 	return nil
 }

+ 67 - 0
transport/config.pb.go

@@ -0,0 +1,67 @@
+// Code generated by protoc-gen-go.
+// source: v2ray.com/core/transport/config.proto
+// DO NOT EDIT!
+
+/*
+Package transport is a generated protocol buffer package.
+
+It is generated from these files:
+	v2ray.com/core/transport/config.proto
+
+It has these top-level messages:
+	Config
+*/
+package transport
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import v2ray_core_transport_internet "v2ray.com/core/transport/internet"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Config struct {
+	NetworkSettings []*v2ray_core_transport_internet.NetworkSettings `protobuf:"bytes,1,rep,name=network_settings,json=networkSettings" json:"network_settings,omitempty"`
+}
+
+func (m *Config) Reset()                    { *m = Config{} }
+func (m *Config) String() string            { return proto.CompactTextString(m) }
+func (*Config) ProtoMessage()               {}
+func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (m *Config) GetNetworkSettings() []*v2ray_core_transport_internet.NetworkSettings {
+	if m != nil {
+		return m.NetworkSettings
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Config)(nil), "v2ray.core.transport.Config")
+}
+
+func init() { proto.RegisterFile("v2ray.com/core/transport/config.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+	// 165 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x33, 0x2a, 0x4a,
+	0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x2f, 0x29, 0x4a, 0xcc, 0x2b,
+	0x2e, 0xc8, 0x2f, 0x2a, 0xd1, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f,
+	0xc9, 0x17, 0x12, 0x81, 0x29, 0x2b, 0x4a, 0xd5, 0x83, 0x2b, 0x91, 0xd2, 0xc3, 0xa9, 0x39, 0x33,
+	0xaf, 0x24, 0xb5, 0x28, 0x2f, 0x15, 0xd5, 0x14, 0xa5, 0x64, 0x2e, 0x36, 0x67, 0x30, 0x5f, 0x28,
+	0x92, 0x4b, 0x20, 0x2f, 0xb5, 0xa4, 0x3c, 0xbf, 0x28, 0x3b, 0xbe, 0x38, 0xb5, 0xa4, 0x24, 0x33,
+	0x2f, 0xbd, 0x58, 0x82, 0x51, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x4f, 0x0f, 0x9b, 0x55, 0x7a, 0x30,
+	0x03, 0xf5, 0xfc, 0x20, 0xda, 0x82, 0xa1, 0xba, 0x82, 0xf8, 0xf3, 0x50, 0x05, 0x9c, 0x8c, 0xb8,
+	0x24, 0x92, 0xf3, 0x73, 0xb1, 0x9a, 0xe2, 0xc4, 0x0d, 0xb1, 0x3e, 0x00, 0xe4, 0x9a, 0x28, 0x4e,
+	0xb8, 0x78, 0x12, 0x1b, 0xd8, 0x7d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0c, 0xfb, 0x2d,
+	0x01, 0x0e, 0x01, 0x00, 0x00,
+}

+ 3 - 1
transport/config.proto

@@ -5,6 +5,8 @@ option go_package = "transport";
 option java_package = "com.v2ray.core.transport";
 option java_outer_classname = "ConfigProto";
 
+import "v2ray.com/core/transport/internet/config.proto";
+
 message Config {
-  
+  repeated v2ray.core.transport.internet.NetworkSettings network_settings = 1;
 }

+ 38 - 4
transport/config_json.go

@@ -5,23 +5,57 @@ package transport
 import (
 	"encoding/json"
 
+	v2net "v2ray.com/core/common/net"
+	"v2ray.com/core/transport/internet"
 	"v2ray.com/core/transport/internet/kcp"
 	"v2ray.com/core/transport/internet/tcp"
 	"v2ray.com/core/transport/internet/ws"
+
+	"github.com/golang/protobuf/ptypes"
 )
 
 func (this *Config) UnmarshalJSON(data []byte) error {
 	type JsonConfig struct {
 		TCPConfig *tcp.Config `json:"tcpSettings"`
-		KCPConfig kcp.Config  `json:"kcpSettings"`
+		KCPConfig *kcp.Config `json:"kcpSettings"`
 		WSConfig  *ws.Config  `json:"wsSettings"`
 	}
 	jsonConfig := &JsonConfig{}
 	if err := json.Unmarshal(data, jsonConfig); err != nil {
 		return err
 	}
-	this.tcpConfig = jsonConfig.TCPConfig
-	this.kcpConfig = jsonConfig.KCPConfig
-	this.wsConfig = jsonConfig.WSConfig
+
+	if jsonConfig.TCPConfig != nil {
+		any, err := ptypes.MarshalAny(jsonConfig.TCPConfig)
+		if err != nil {
+			return err
+		}
+		this.NetworkSettings = append(this.NetworkSettings, &internet.NetworkSettings{
+			Network:  v2net.Network_TCP,
+			Settings: any,
+		})
+	}
+
+	if jsonConfig.KCPConfig != nil {
+		any, err := ptypes.MarshalAny(jsonConfig.KCPConfig)
+		if err != nil {
+			return err
+		}
+		this.NetworkSettings = append(this.NetworkSettings, &internet.NetworkSettings{
+			Network:  v2net.Network_KCP,
+			Settings: any,
+		})
+	}
+
+	if jsonConfig.WSConfig != nil {
+		any, err := ptypes.MarshalAny(jsonConfig.WSConfig)
+		if err != nil {
+			return err
+		}
+		this.NetworkSettings = append(this.NetworkSettings, &internet.NetworkSettings{
+			Network:  v2net.Network_WebSocket,
+			Settings: any,
+		})
+	}
 	return nil
 }

+ 4 - 0
transport/internet/authenticator.pb.go

@@ -7,9 +7,13 @@ Package internet is a generated protocol buffer package.
 
 It is generated from these files:
 	v2ray.com/core/transport/internet/authenticator.proto
+	v2ray.com/core/transport/internet/config.proto
 
 It has these top-level messages:
 	AuthenticatorConfig
+	SecuritySettings
+	NetworkSettings
+	StreamConfig
 */
 package internet
 

+ 108 - 0
transport/internet/config.go

@@ -0,0 +1,108 @@
+package internet
+
+import (
+	"errors"
+
+	"v2ray.com/core/common/log"
+	v2net "v2ray.com/core/common/net"
+	v2tls "v2ray.com/core/transport/internet/tls"
+
+	"github.com/golang/protobuf/proto"
+	"github.com/golang/protobuf/ptypes"
+)
+
+type NetworkConfigCreator func() proto.Message
+
+var (
+	globalNetworkConfigCreatorCache  = make(map[v2net.Network]NetworkConfigCreator)
+	globalSecurityConfigCreatorCache = make(map[SecurityType]NetworkConfigCreator)
+
+	globalNetworkSettings []*NetworkSettings
+
+	ErrUnconfiguredNetwork = errors.New("Network config creator not set.")
+)
+
+func RegisterNetworkConfigCreator(network v2net.Network, creator NetworkConfigCreator) error {
+	// TODO: check duplicate
+	globalNetworkConfigCreatorCache[network] = creator
+	return nil
+}
+
+func CreateNetworkConfig(network v2net.Network) (proto.Message, error) {
+	creator, ok := globalNetworkConfigCreatorCache[network]
+	if !ok {
+		log.Warning("Internet: Network config creator not found: ", network)
+		return nil, ErrUnconfiguredNetwork
+	}
+	return creator(), nil
+}
+
+func RegisterSecurityConfigCreator(securityType SecurityType, creator NetworkConfigCreator) error {
+	globalSecurityConfigCreatorCache[securityType] = creator
+	return nil
+}
+
+func CreateSecurityConfig(securityType SecurityType) (proto.Message, error) {
+	creator, ok := globalSecurityConfigCreatorCache[securityType]
+	if !ok {
+		log.Warning("Internet: Security config creator not found: ", securityType)
+		return nil, ErrUnconfiguredNetwork
+	}
+	return creator(), nil
+}
+
+func (this *NetworkSettings) GetTypedSettings() (interface{}, error) {
+	message, err := CreateNetworkConfig(this.Network)
+	if err != nil {
+		return nil, err
+	}
+	if err := ptypes.UnmarshalAny(this.Settings, message); err != nil {
+		return nil, err
+	}
+	return message, nil
+}
+
+func (this *SecuritySettings) GetTypeSettings() (interface{}, error) {
+	message, err := CreateSecurityConfig(this.Type)
+	if err != nil {
+		return nil, err
+	}
+	if err := ptypes.UnmarshalAny(this.Settings, message); err != nil {
+		return nil, err
+	}
+	return message, nil
+}
+
+func (this *StreamConfig) GetEffectiveNetworkSettings() (interface{}, error) {
+	for _, settings := range this.NetworkSettings {
+		if settings.Network == this.Network {
+			return settings.GetTypedSettings()
+		}
+	}
+	for _, settings := range globalNetworkSettings {
+		if settings.Network == this.Network {
+			return settings.GetTypedSettings()
+		}
+	}
+	return CreateNetworkConfig(this.Network)
+}
+
+func (this *StreamConfig) GetEffectiveSecuritySettings() (interface{}, error) {
+	for _, settings := range this.SecuritySettings {
+		if settings.Type == this.SecurityType {
+			return settings.GetTypeSettings()
+		}
+	}
+	return CreateSecurityConfig(this.SecurityType)
+}
+
+func ApplyGlobalNetworkSettings(settings []*NetworkSettings) error {
+	globalNetworkSettings = settings
+	return nil
+}
+
+func init() {
+	RegisterSecurityConfigCreator(SecurityType_TLS, func() proto.Message {
+		return new(v2tls.Config)
+	})
+}

+ 132 - 0
transport/internet/config.pb.go

@@ -0,0 +1,132 @@
+// Code generated by protoc-gen-go.
+// source: v2ray.com/core/transport/internet/config.proto
+// DO NOT EDIT!
+
+package internet
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import v2ray_core_common_net "v2ray.com/core/common/net"
+import google_protobuf "github.com/golang/protobuf/ptypes/any"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+type SecurityType int32
+
+const (
+	SecurityType_None SecurityType = 0
+	SecurityType_TLS  SecurityType = 1
+)
+
+var SecurityType_name = map[int32]string{
+	0: "None",
+	1: "TLS",
+}
+var SecurityType_value = map[string]int32{
+	"None": 0,
+	"TLS":  1,
+}
+
+func (x SecurityType) String() string {
+	return proto.EnumName(SecurityType_name, int32(x))
+}
+func (SecurityType) EnumDescriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
+
+type SecuritySettings struct {
+	Type     SecurityType         `protobuf:"varint,1,opt,name=type,enum=v2ray.core.transport.internet.SecurityType" json:"type,omitempty"`
+	Settings *google_protobuf.Any `protobuf:"bytes,2,opt,name=settings" json:"settings,omitempty"`
+}
+
+func (m *SecuritySettings) Reset()                    { *m = SecuritySettings{} }
+func (m *SecuritySettings) String() string            { return proto.CompactTextString(m) }
+func (*SecuritySettings) ProtoMessage()               {}
+func (*SecuritySettings) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
+
+func (m *SecuritySettings) GetSettings() *google_protobuf.Any {
+	if m != nil {
+		return m.Settings
+	}
+	return nil
+}
+
+type NetworkSettings struct {
+	Network  v2ray_core_common_net.Network `protobuf:"varint,1,opt,name=network,enum=v2ray.core.common.net.Network" json:"network,omitempty"`
+	Settings *google_protobuf.Any          `protobuf:"bytes,2,opt,name=settings" json:"settings,omitempty"`
+}
+
+func (m *NetworkSettings) Reset()                    { *m = NetworkSettings{} }
+func (m *NetworkSettings) String() string            { return proto.CompactTextString(m) }
+func (*NetworkSettings) ProtoMessage()               {}
+func (*NetworkSettings) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} }
+
+func (m *NetworkSettings) GetSettings() *google_protobuf.Any {
+	if m != nil {
+		return m.Settings
+	}
+	return nil
+}
+
+type StreamConfig struct {
+	Network          v2ray_core_common_net.Network `protobuf:"varint,1,opt,name=network,enum=v2ray.core.common.net.Network" json:"network,omitempty"`
+	NetworkSettings  []*NetworkSettings            `protobuf:"bytes,2,rep,name=network_settings,json=networkSettings" json:"network_settings,omitempty"`
+	SecurityType     SecurityType                  `protobuf:"varint,3,opt,name=security_type,json=securityType,enum=v2ray.core.transport.internet.SecurityType" json:"security_type,omitempty"`
+	SecuritySettings []*SecuritySettings           `protobuf:"bytes,4,rep,name=security_settings,json=securitySettings" json:"security_settings,omitempty"`
+}
+
+func (m *StreamConfig) Reset()                    { *m = StreamConfig{} }
+func (m *StreamConfig) String() string            { return proto.CompactTextString(m) }
+func (*StreamConfig) ProtoMessage()               {}
+func (*StreamConfig) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} }
+
+func (m *StreamConfig) GetNetworkSettings() []*NetworkSettings {
+	if m != nil {
+		return m.NetworkSettings
+	}
+	return nil
+}
+
+func (m *StreamConfig) GetSecuritySettings() []*SecuritySettings {
+	if m != nil {
+		return m.SecuritySettings
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*SecuritySettings)(nil), "v2ray.core.transport.internet.SecuritySettings")
+	proto.RegisterType((*NetworkSettings)(nil), "v2ray.core.transport.internet.NetworkSettings")
+	proto.RegisterType((*StreamConfig)(nil), "v2ray.core.transport.internet.StreamConfig")
+	proto.RegisterEnum("v2ray.core.transport.internet.SecurityType", SecurityType_name, SecurityType_value)
+}
+
+func init() { proto.RegisterFile("v2ray.com/core/transport/internet/config.proto", fileDescriptor1) }
+
+var fileDescriptor1 = []byte{
+	// 347 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x91, 0x4f, 0x4b, 0xc3, 0x40,
+	0x10, 0xc5, 0x4d, 0x5b, 0x6c, 0x99, 0x56, 0x1b, 0x83, 0x87, 0x5a, 0x50, 0xda, 0x5e, 0x0c, 0x8a,
+	0xbb, 0x12, 0x2f, 0xde, 0x44, 0xbd, 0x4a, 0x91, 0xa4, 0x17, 0x45, 0x28, 0x69, 0x98, 0x86, 0xa0,
+	0xd9, 0x09, 0x9b, 0xad, 0x92, 0x83, 0x47, 0x3f, 0xa1, 0x5f, 0x48, 0x9a, 0x7f, 0x84, 0x1c, 0xea,
+	0xbf, 0xdb, 0x6e, 0xf2, 0xe6, 0xcd, 0x6f, 0xdf, 0x03, 0xf6, 0x6a, 0x49, 0x37, 0x61, 0x1e, 0x85,
+	0xdc, 0x23, 0x89, 0x5c, 0x49, 0x57, 0xc4, 0x11, 0x49, 0xc5, 0x03, 0xa1, 0x50, 0x0a, 0x54, 0xdc,
+	0x23, 0xb1, 0x0c, 0x7c, 0x16, 0x49, 0x52, 0x64, 0x1c, 0x16, 0x7a, 0x89, 0xac, 0xd4, 0xb2, 0x42,
+	0x3b, 0x3c, 0xae, 0xd9, 0x79, 0x14, 0x86, 0x24, 0xf8, 0xda, 0x46, 0xa0, 0x7a, 0x23, 0xf9, 0x9c,
+	0xf9, 0x0c, 0x0f, 0x7c, 0x22, 0xff, 0x05, 0x79, 0x7a, 0x5b, 0xac, 0x96, 0xdc, 0x15, 0x49, 0xf6,
+	0x6b, 0xf2, 0xa1, 0x81, 0xee, 0xa0, 0xb7, 0x92, 0x81, 0x4a, 0x1c, 0x54, 0x2a, 0x10, 0x7e, 0x6c,
+	0x5c, 0x41, 0x4b, 0x25, 0x11, 0x0e, 0xb4, 0x91, 0x66, 0xee, 0x5a, 0xa7, 0x6c, 0x23, 0x06, 0x2b,
+	0xc6, 0x67, 0x49, 0x84, 0x76, 0x3a, 0x68, 0x9c, 0x43, 0x27, 0xce, 0xcd, 0x06, 0x8d, 0x91, 0x66,
+	0x76, 0xad, 0x7d, 0x96, 0x31, 0xb0, 0x82, 0x81, 0x5d, 0x8b, 0xc4, 0x2e, 0x55, 0x93, 0x77, 0xe8,
+	0x4f, 0x33, 0xe6, 0x92, 0xe2, 0x12, 0xda, 0xf9, 0x33, 0x72, 0x90, 0xa3, 0x2a, 0x48, 0xf6, 0x58,
+	0xb6, 0x06, 0xc8, 0x07, 0xed, 0x42, 0xfe, 0x87, 0xf5, 0x9f, 0x0d, 0xe8, 0x39, 0x4a, 0xa2, 0x1b,
+	0xde, 0xa6, 0x05, 0xfc, 0x63, 0xf9, 0x03, 0xe8, 0xf9, 0x71, 0x5e, 0x81, 0x68, 0x9a, 0x5d, 0x8b,
+	0x7d, 0x13, 0x64, 0x2d, 0x00, 0xbb, 0x2f, 0x6a, 0x89, 0xdc, 0xc3, 0x4e, 0x9c, 0x87, 0x3d, 0x4f,
+	0x0b, 0x6a, 0xfe, 0xbe, 0xa0, 0x5e, 0x5c, 0xb9, 0x19, 0x4f, 0xb0, 0x57, 0x3a, 0x96, 0xb4, 0xad,
+	0x94, 0x96, 0xff, 0xd0, 0xb5, 0xc4, 0xd5, 0xe3, 0xda, 0x97, 0x93, 0x31, 0xf4, 0xaa, 0xbb, 0x8d,
+	0x0e, 0xb4, 0xa6, 0x24, 0x50, 0xdf, 0x32, 0xda, 0xd0, 0x9c, 0xdd, 0x39, 0xba, 0x76, 0x73, 0x06,
+	0x63, 0x8f, 0xc2, 0xcd, 0xab, 0x1e, 0x3b, 0xc5, 0x69, 0xb1, 0x9d, 0xf6, 0x77, 0xf1, 0x15, 0x00,
+	0x00, 0xff, 0xff, 0xb2, 0x2e, 0x36, 0x52, 0x4a, 0x03, 0x00, 0x00,
+}

+ 31 - 0
transport/internet/config.proto

@@ -0,0 +1,31 @@
+syntax = "proto3";
+
+package v2ray.core.transport.internet;
+option go_package = "internet";
+option java_package = "com.v2ray.core.transport.internet";
+
+import "v2ray.com/core/common/net/network.proto";
+
+import "google/protobuf/any.proto";
+
+enum SecurityType {
+  None = 0;
+  TLS = 1;
+}
+
+message SecuritySettings {
+  SecurityType type = 1;
+  google.protobuf.Any settings = 2;
+}
+
+message NetworkSettings {
+  v2ray.core.common.net.Network network = 1;
+  google.protobuf.Any settings = 2;
+}
+
+message StreamConfig {
+  v2ray.core.common.net.Network network = 1;
+  repeated NetworkSettings network_settings = 2;
+  SecurityType security_type = 3;
+  repeated SecuritySettings security_settings = 4;
+}

+ 0 - 51
transport/internet/connection.go

@@ -1,7 +1,6 @@
 package internet
 
 import (
-	"crypto/tls"
 	"net"
 )
 
@@ -12,56 +11,6 @@ type Reusable interface {
 	SetReusable(reuse bool)
 }
 
-type StreamConnectionType int
-
-const (
-	StreamConnectionTypeRawTCP    StreamConnectionType = 1
-	StreamConnectionTypeTCP       StreamConnectionType = 2
-	StreamConnectionTypeKCP       StreamConnectionType = 4
-	StreamConnectionTypeWebSocket StreamConnectionType = 8
-)
-
-type StreamSecurityType int
-
-const (
-	StreamSecurityTypeNone StreamSecurityType = 0
-	StreamSecurityTypeTLS  StreamSecurityType = 1
-)
-
-var (
-	globalSessionCache = tls.NewLRUClientSessionCache(128)
-)
-
-type TLSSettings struct {
-	AllowInsecure bool
-	Certs         []tls.Certificate
-}
-
-func (this *TLSSettings) GetTLSConfig() *tls.Config {
-	config := &tls.Config{
-		ClientSessionCache: globalSessionCache,
-	}
-	if this == nil {
-		return config
-	}
-
-	config.InsecureSkipVerify = this.AllowInsecure
-	config.Certificates = this.Certs
-	config.BuildNameToCertificate()
-
-	return config
-}
-
-type StreamSettings struct {
-	Type        StreamConnectionType
-	Security    StreamSecurityType
-	TLSSettings *TLSSettings
-}
-
-func (this *StreamSettings) IsCapableOf(streamType StreamConnectionType) bool {
-	return (this.Type & streamType) == streamType
-}
-
 type Connection interface {
 	net.Conn
 	Reusable

+ 20 - 43
transport/internet/connection_json.go

@@ -3,65 +3,42 @@
 package internet
 
 import (
-	"crypto/tls"
 	"encoding/json"
-	"errors"
 	"strings"
 
+	"errors"
+	"github.com/golang/protobuf/ptypes"
 	v2net "v2ray.com/core/common/net"
+	v2tls "v2ray.com/core/transport/internet/tls"
 )
 
-func (this *TLSSettings) UnmarshalJSON(data []byte) error {
-	type JSONCertConfig struct {
-		CertFile string `json:"certificateFile"`
-		KeyFile  string `json:"keyFile"`
-	}
+func (this *StreamConfig) UnmarshalJSON(data []byte) error {
 	type JSONConfig struct {
-		Insecure bool              `json:"allowInsecure"`
-		Certs    []*JSONCertConfig `json:"certificates"`
+		Network     *v2net.Network `json:"network"`
+		Security    string         `json:"security"`
+		TLSSettings *v2tls.Config  `json:"tlsSettings"`
 	}
+	this.Network = v2net.Network_RawTCP
 	jsonConfig := new(JSONConfig)
 	if err := json.Unmarshal(data, jsonConfig); err != nil {
 		return err
 	}
-	this.Certs = make([]tls.Certificate, len(jsonConfig.Certs))
-	for idx, certConf := range jsonConfig.Certs {
-		cert, err := tls.LoadX509KeyPair(certConf.CertFile, certConf.KeyFile)
-		if err != nil {
-			return errors.New("Internet|TLS: Failed to load certificate file: " + err.Error())
-		}
-		this.Certs[idx] = cert
-	}
-	this.AllowInsecure = jsonConfig.Insecure
-	return nil
-}
-
-func (this *StreamSettings) UnmarshalJSON(data []byte) error {
-	type JSONConfig struct {
-		Network     v2net.NetworkList `json:"network"`
-		Security    string            `json:"security"`
-		TLSSettings *TLSSettings      `json:"tlsSettings"`
+	if jsonConfig.Network != nil {
+		this.Network = *jsonConfig.Network
 	}
-	this.Type = StreamConnectionTypeRawTCP
-	jsonConfig := new(JSONConfig)
-	if err := json.Unmarshal(data, jsonConfig); err != nil {
-		return err
-	}
-	if jsonConfig.Network.HasNetwork(v2net.Network_KCP) {
-		this.Type |= StreamConnectionTypeKCP
-	}
-	if jsonConfig.Network.HasNetwork(v2net.Network_WebSocket) {
-		this.Type |= StreamConnectionTypeWebSocket
-	}
-	if jsonConfig.Network.HasNetwork(v2net.Network_TCP) {
-		this.Type |= StreamConnectionTypeTCP
-	}
-	this.Security = StreamSecurityTypeNone
+	this.SecurityType = SecurityType_None
 	if strings.ToLower(jsonConfig.Security) == "tls" {
-		this.Security = StreamSecurityTypeTLS
+		this.SecurityType = SecurityType_TLS
 	}
 	if jsonConfig.TLSSettings != nil {
-		this.TLSSettings = jsonConfig.TLSSettings
+		anyTLSSettings, err := ptypes.MarshalAny(jsonConfig.TLSSettings)
+		if err != nil {
+			return errors.New("Internet: Failed to parse TLS settings: " + err.Error())
+		}
+		this.SecuritySettings = append(this.SecuritySettings, &SecuritySettings{
+			Type:     SecurityType_TLS,
+			Settings: anyTLSSettings,
+		})
 	}
 	return nil
 }

+ 7 - 7
transport/internet/dialer.go

@@ -12,7 +12,7 @@ var (
 )
 
 type DialerOptions struct {
-	Stream *StreamSettings
+	Stream *StreamConfig
 }
 
 type Dialer func(src v2net.Address, dest v2net.Destination, options DialerOptions) (Connection, error)
@@ -25,7 +25,7 @@ var (
 	WSDialer     Dialer
 )
 
-func Dial(src v2net.Address, dest v2net.Destination, settings *StreamSettings) (Connection, error) {
+func Dial(src v2net.Address, dest v2net.Destination, settings *StreamConfig) (Connection, error) {
 
 	var connection Connection
 	var err error
@@ -33,16 +33,16 @@ func Dial(src v2net.Address, dest v2net.Destination, settings *StreamSettings) (
 		Stream: settings,
 	}
 	if dest.Network == v2net.Network_TCP {
-		switch {
-		case settings.IsCapableOf(StreamConnectionTypeTCP):
+		switch settings.Network {
+		case v2net.Network_TCP:
 			connection, err = TCPDialer(src, dest, dialerOptions)
-		case settings.IsCapableOf(StreamConnectionTypeKCP):
+		case v2net.Network_KCP:
 			connection, err = KCPDialer(src, dest, dialerOptions)
-		case settings.IsCapableOf(StreamConnectionTypeWebSocket):
+		case v2net.Network_WebSocket:
 			connection, err = WSDialer(src, dest, dialerOptions)
 
 			// This check has to be the last one.
-		case settings.IsCapableOf(StreamConnectionTypeRawTCP):
+		case v2net.Network_RawTCP:
 			connection, err = RawTCPDialer(src, dest, dialerOptions)
 		default:
 			return nil, ErrUnsupportedStreamType

+ 8 - 7
transport/internet/kcp/config.go

@@ -1,7 +1,10 @@
 package kcp
 
 import (
+	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/transport/internet"
+
+	"github.com/golang/protobuf/proto"
 )
 
 func (this *MTU) GetValue() uint32 {
@@ -46,10 +49,6 @@ func (this *ReadBuffer) GetSize() uint32 {
 	return this.Size
 }
 
-func (this *Config) Apply() {
-	effectiveConfig = *this
-}
-
 func (this *Config) GetAuthenticator() (internet.Authenticator, error) {
 	auth := NewSimpleAuthenticator()
 	if this.HeaderConfig != nil {
@@ -86,6 +85,8 @@ func (this *Config) GetReceivingBufferSize() uint32 {
 	return this.GetReceivingInFlightSize() + this.ReadBuffer.GetSize()/this.Mtu.GetValue()
 }
 
-var (
-	effectiveConfig Config
-)
+func init() {
+	internet.RegisterNetworkConfigCreator(v2net.Network_KCP, func() proto.Message {
+		return new(Config)
+	})
+}

+ 8 - 5
transport/internet/kcp/connection.go

@@ -129,6 +129,7 @@ type Connection struct {
 	since          int64
 	dataInputCond  *sync.Cond
 	dataOutputCond *sync.Cond
+	Config         *Config
 
 	conv             uint16
 	state            State
@@ -149,7 +150,7 @@ type Connection struct {
 }
 
 // NewConnection create a new KCP connection between local and remote.
-func NewConnection(conv uint16, writerCloser io.WriteCloser, local *net.UDPAddr, remote *net.UDPAddr, block internet.Authenticator) *Connection {
+func NewConnection(conv uint16, writerCloser io.WriteCloser, local *net.UDPAddr, remote *net.UDPAddr, block internet.Authenticator, config *Config) *Connection {
 	log.Info("KCP|Connection: creating connection ", conv)
 
 	conn := new(Connection)
@@ -160,10 +161,12 @@ func NewConnection(conv uint16, writerCloser io.WriteCloser, local *net.UDPAddr,
 	conn.since = nowMillisec()
 	conn.dataInputCond = sync.NewCond(new(sync.Mutex))
 	conn.dataOutputCond = sync.NewCond(new(sync.Mutex))
+	conn.Config = config
 
 	authWriter := &AuthenticationWriter{
 		Authenticator: block,
 		Writer:        writerCloser,
+		Config:        config,
 	}
 	conn.conv = conv
 	conn.output = NewSegmentWriter(authWriter)
@@ -171,12 +174,12 @@ func NewConnection(conv uint16, writerCloser io.WriteCloser, local *net.UDPAddr,
 	conn.mss = authWriter.Mtu() - DataSegmentOverhead
 	conn.roundTrip = &RoundTripInfo{
 		rto:    100,
-		minRtt: effectiveConfig.Tti.GetValue(),
+		minRtt: config.Tti.GetValue(),
 	}
-	conn.interval = effectiveConfig.Tti.GetValue()
+	conn.interval = config.Tti.GetValue()
 	conn.receivingWorker = NewReceivingWorker(conn)
 	conn.fastresend = 2
-	conn.congestionControl = effectiveConfig.Congestion
+	conn.congestionControl = config.Congestion
 	conn.sendingWorker = NewSendingWorker(conn)
 
 	go conn.updateTask()
@@ -366,7 +369,7 @@ func (this *Connection) updateTask() {
 	for this.State() != StateTerminated {
 		this.flush()
 
-		interval := time.Duration(effectiveConfig.Tti.GetValue()) * time.Millisecond
+		interval := time.Duration(this.Config.Tti.GetValue()) * time.Millisecond
 		if this.State() == StateTerminating {
 			interval = time.Second
 		}

+ 3 - 3
transport/internet/kcp/connection_test.go

@@ -27,7 +27,7 @@ func (this *NoOpWriteCloser) Close() error {
 func TestConnectionReadTimeout(t *testing.T) {
 	assert := assert.On(t)
 
-	conn := NewConnection(1, &NoOpWriteCloser{}, nil, nil, NewSimpleAuthenticator())
+	conn := NewConnection(1, &NoOpWriteCloser{}, nil, nil, NewSimpleAuthenticator(), &Config{})
 	conn.SetReadDeadline(time.Now().Add(time.Second))
 
 	b := make([]byte, 1024)
@@ -44,10 +44,10 @@ func TestConnectionReadWrite(t *testing.T) {
 
 	auth := internet.NewAuthenticatorChain(srtp.SRTPFactory{}.Create(nil), NewSimpleAuthenticator())
 
-	connClient := NewConnection(1, upWriter, &net.UDPAddr{IP: v2net.LocalHostIP.IP(), Port: 1}, &net.UDPAddr{IP: v2net.LocalHostIP.IP(), Port: 2}, auth)
+	connClient := NewConnection(1, upWriter, &net.UDPAddr{IP: v2net.LocalHostIP.IP(), Port: 1}, &net.UDPAddr{IP: v2net.LocalHostIP.IP(), Port: 2}, auth, &Config{})
 	connClient.FetchInputFrom(downReader)
 
-	connServer := NewConnection(1, downWriter, &net.UDPAddr{IP: v2net.LocalHostIP.IP(), Port: 2}, &net.UDPAddr{IP: v2net.LocalHostIP.IP(), Port: 1}, auth)
+	connServer := NewConnection(1, downWriter, &net.UDPAddr{IP: v2net.LocalHostIP.IP(), Port: 2}, &net.UDPAddr{IP: v2net.LocalHostIP.IP(), Port: 1}, auth, &Config{})
 	connServer.FetchInputFrom(upReader)
 
 	totalWritten := 1024 * 1024

+ 16 - 4
transport/internet/kcp/dialer.go

@@ -25,20 +25,32 @@ func DialKCP(src v2net.Address, dest v2net.Destination, options internet.DialerO
 		return nil, err
 	}
 
-	cpip, err := effectiveConfig.GetAuthenticator()
+	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
+	if err != nil {
+		log.Error("KCP|Dialer: Failed to get KCP settings: ", err)
+		return nil, err
+	}
+	kcpSettings := networkSettings.(*Config)
+
+	cpip, err := kcpSettings.GetAuthenticator()
 	if err != nil {
 		log.Error("KCP|Dialer: Failed to create authenticator: ", err)
 		return nil, err
 	}
 	conv := uint16(atomic.AddUint32(&globalConv, 1))
-	session := NewConnection(conv, conn, conn.LocalAddr().(*net.UDPAddr), conn.RemoteAddr().(*net.UDPAddr), cpip)
+	session := NewConnection(conv, conn, conn.LocalAddr().(*net.UDPAddr), conn.RemoteAddr().(*net.UDPAddr), cpip, kcpSettings)
 	session.FetchInputFrom(conn)
 
 	var iConn internet.Connection
 	iConn = session
 
-	if options.Stream != nil && options.Stream.Security == internet.StreamSecurityTypeTLS {
-		config := options.Stream.TLSSettings.GetTLSConfig()
+	if options.Stream != nil && options.Stream.SecurityType == internet.SecurityType_TLS {
+		securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
+		if err != nil {
+			log.Error("KCP|Dialer: Failed to apply TLS config: ", err)
+			return nil, err
+		}
+		config := securitySettings.(*v2tls.Config).GetTLSConfig()
 		if dest.Address.Family().IsDomain() {
 			config.ServerName = dest.Address.Domain()
 		}

+ 28 - 2
transport/internet/kcp/kcp_test.go

@@ -12,12 +12,28 @@ import (
 	"v2ray.com/core/testing/assert"
 	"v2ray.com/core/transport/internet"
 	. "v2ray.com/core/transport/internet/kcp"
+
+	"github.com/golang/protobuf/ptypes"
 )
 
 func TestDialAndListen(t *testing.T) {
 	assert := assert.On(t)
 
-	listerner, err := NewListener(v2net.LocalHostIP, v2net.Port(0), internet.ListenOptions{})
+	kcpSettings := new(Config)
+	anySettings, err := ptypes.MarshalAny(kcpSettings)
+	assert.Error(err).IsNil()
+
+	listerner, err := NewListener(v2net.LocalHostIP, v2net.Port(0), internet.ListenOptions{
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_KCP,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network:  v2net.Network_KCP,
+					Settings: anySettings,
+				},
+			},
+		},
+	})
 	assert.Error(err).IsNil()
 	port := v2net.Port(listerner.Addr().(*net.UDPAddr).Port)
 
@@ -46,7 +62,17 @@ func TestDialAndListen(t *testing.T) {
 
 	wg := new(sync.WaitGroup)
 	for i := 0; i < 10; i++ {
-		clientConn, err := DialKCP(v2net.LocalHostIP, v2net.UDPDestination(v2net.LocalHostIP, port), internet.DialerOptions{})
+		clientConn, err := DialKCP(v2net.LocalHostIP, v2net.UDPDestination(v2net.LocalHostIP, port), internet.DialerOptions{
+			Stream: &internet.StreamConfig{
+				Network: v2net.Network_KCP,
+				NetworkSettings: []*internet.NetworkSettings{
+					{
+						Network:  v2net.Network_KCP,
+						Settings: anySettings,
+					},
+				},
+			},
+		})
 		assert.Error(err).IsNil()
 		wg.Add(1)
 

+ 19 - 5
transport/internet/kcp/listener.go

@@ -25,10 +25,18 @@ type Listener struct {
 	awaitingConns chan *Connection
 	hub           *udp.UDPHub
 	tlsConfig     *tls.Config
+	config        *Config
 }
 
 func NewListener(address v2net.Address, port v2net.Port, options internet.ListenOptions) (*Listener, error) {
-	auth, err := effectiveConfig.GetAuthenticator()
+	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
+	if err != nil {
+		log.Error("KCP|Listener: Failed to get KCP settings: ", err)
+		return nil, err
+	}
+	kcpSettings := networkSettings.(*Config)
+
+	auth, err := kcpSettings.GetAuthenticator()
 	if err != nil {
 		return nil, err
 	}
@@ -37,9 +45,15 @@ func NewListener(address v2net.Address, port v2net.Port, options internet.Listen
 		sessions:      make(map[string]*Connection),
 		awaitingConns: make(chan *Connection, 64),
 		running:       true,
+		config:        kcpSettings,
 	}
-	if options.Stream != nil && options.Stream.Security == internet.StreamSecurityTypeTLS {
-		l.tlsConfig = options.Stream.TLSSettings.GetTLSConfig()
+	if options.Stream != nil && options.Stream.SecurityType == internet.SecurityType_TLS {
+		securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
+		if err != nil {
+			log.Error("KCP|Listener: Failed to apply TLS config: ", err)
+			return nil, err
+		}
+		l.tlsConfig = securitySettings.(*v2tls.Config).GetTLSConfig()
 	}
 	hub, err := udp.ListenUDP(address, port, udp.ListenOption{Callback: l.OnReceive})
 	if err != nil {
@@ -89,11 +103,11 @@ func (this *Listener) OnReceive(payload *alloc.Buffer, session *proxy.SessionInf
 			IP:   src.Address.IP(),
 			Port: int(src.Port),
 		}
-		auth, err := effectiveConfig.GetAuthenticator()
+		auth, err := this.config.GetAuthenticator()
 		if err != nil {
 			log.Error("KCP|Listener: Failed to create authenticator: ", err)
 		}
-		conn = NewConnection(conv, writer, this.Addr().(*net.UDPAddr), srcAddr, auth)
+		conn = NewConnection(conv, writer, this.Addr().(*net.UDPAddr), srcAddr, auth, this.config)
 		select {
 		case this.awaitingConns <- conn:
 		case <-time.After(time.Second * 5):

+ 2 - 1
transport/internet/kcp/output.go

@@ -62,6 +62,7 @@ func (this *BufferedSegmentWriter) Flush() {
 type AuthenticationWriter struct {
 	Authenticator internet.Authenticator
 	Writer        io.Writer
+	Config        *Config
 }
 
 func (this *AuthenticationWriter) Write(payload *alloc.Buffer) error {
@@ -75,5 +76,5 @@ func (this *AuthenticationWriter) Write(payload *alloc.Buffer) error {
 func (this *AuthenticationWriter) Release() {}
 
 func (this *AuthenticationWriter) Mtu() uint32 {
-	return effectiveConfig.Mtu.GetValue() - uint32(this.Authenticator.Overhead())
+	return this.Config.Mtu.GetValue() - uint32(this.Authenticator.Overhead())
 }

+ 2 - 2
transport/internet/kcp/receiving.go

@@ -124,8 +124,8 @@ type ReceivingWorker struct {
 func NewReceivingWorker(kcp *Connection) *ReceivingWorker {
 	worker := &ReceivingWorker{
 		conn:       kcp,
-		window:     NewReceivingWindow(effectiveConfig.GetReceivingBufferSize()),
-		windowSize: effectiveConfig.GetReceivingInFlightSize(),
+		window:     NewReceivingWindow(kcp.Config.GetReceivingBufferSize()),
+		windowSize: kcp.Config.GetReceivingInFlightSize(),
 	}
 	worker.acklist = NewAckList(worker)
 	return worker

+ 7 - 7
transport/internet/kcp/sending.go

@@ -185,9 +185,9 @@ func NewSendingWorker(kcp *Connection) *SendingWorker {
 		conn:             kcp,
 		fastResend:       2,
 		remoteNextNumber: 32,
-		controlWindow:    effectiveConfig.GetSendingInFlightSize(),
+		controlWindow:    kcp.Config.GetSendingInFlightSize(),
 	}
-	worker.window = NewSendingWindow(effectiveConfig.GetSendingBufferSize(), worker, worker.OnPacketLoss)
+	worker.window = NewSendingWindow(kcp.Config.GetSendingBufferSize(), worker, worker.OnPacketLoss)
 	return worker
 }
 
@@ -291,7 +291,7 @@ func (this *SendingWorker) Write(seg Segment) {
 }
 
 func (this *SendingWorker) OnPacketLoss(lossRate uint32) {
-	if !effectiveConfig.Congestion || this.conn.roundTrip.Timeout() == 0 {
+	if !this.conn.Config.Congestion || this.conn.roundTrip.Timeout() == 0 {
 		return
 	}
 
@@ -303,8 +303,8 @@ func (this *SendingWorker) OnPacketLoss(lossRate uint32) {
 	if this.controlWindow < 16 {
 		this.controlWindow = 16
 	}
-	if this.controlWindow > 2*effectiveConfig.GetSendingInFlightSize() {
-		this.controlWindow = 2 * effectiveConfig.GetSendingInFlightSize()
+	if this.controlWindow > 2*this.conn.Config.GetSendingInFlightSize() {
+		this.controlWindow = 2 * this.conn.Config.GetSendingInFlightSize()
 	}
 }
 
@@ -312,11 +312,11 @@ func (this *SendingWorker) Flush(current uint32) {
 	this.Lock()
 	defer this.Unlock()
 
-	cwnd := this.firstUnacknowledged + effectiveConfig.GetSendingInFlightSize()
+	cwnd := this.firstUnacknowledged + this.conn.Config.GetSendingInFlightSize()
 	if cwnd > this.remoteNextNumber {
 		cwnd = this.remoteNextNumber
 	}
-	if effectiveConfig.Congestion && cwnd > this.firstUnacknowledged+this.controlWindow {
+	if this.conn.Config.Congestion && cwnd > this.firstUnacknowledged+this.controlWindow {
 		cwnd = this.firstUnacknowledged + this.controlWindow
 	}
 

+ 10 - 11
transport/internet/tcp/config.go

@@ -1,15 +1,14 @@
 package tcp
 
-type Config struct {
-	ConnectionReuse bool
-}
-
-func (this *Config) Apply() {
-	effectiveConfig = this
-}
+import (
+	v2net "v2ray.com/core/common/net"
+	"v2ray.com/core/transport/internet"
 
-var (
-	effectiveConfig = &Config{
-		ConnectionReuse: true,
-	}
+	"github.com/golang/protobuf/proto"
 )
+
+func init() {
+	internet.RegisterNetworkConfigCreator(v2net.Network_TCP, func() proto.Message {
+		return new(Config)
+	})
+}

+ 58 - 0
transport/internet/tcp/config.pb.go

@@ -0,0 +1,58 @@
+// Code generated by protoc-gen-go.
+// source: v2ray.com/core/transport/internet/tcp/config.proto
+// DO NOT EDIT!
+
+/*
+Package tcp is a generated protocol buffer package.
+
+It is generated from these files:
+	v2ray.com/core/transport/internet/tcp/config.proto
+
+It has these top-level messages:
+	Config
+*/
+package tcp
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Config struct {
+	ConnectionReuse bool `protobuf:"varint,1,opt,name=connection_reuse,json=connectionReuse" json:"connection_reuse,omitempty"`
+}
+
+func (m *Config) Reset()                    { *m = Config{} }
+func (m *Config) String() string            { return proto.CompactTextString(m) }
+func (*Config) ProtoMessage()               {}
+func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func init() {
+	proto.RegisterType((*Config)(nil), "v2ray.core.transport.internet.tcp.Config")
+}
+
+func init() { proto.RegisterFile("v2ray.com/core/transport/internet/tcp/config.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+	// 158 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x8e, 0xb1, 0x0a, 0xc2, 0x50,
+	0x0c, 0x45, 0x29, 0x42, 0x91, 0xe7, 0xa0, 0x74, 0x72, 0x54, 0x41, 0xd0, 0x25, 0x0f, 0xda, 0xc9,
+	0xb5, 0xfe, 0x80, 0x74, 0x74, 0x91, 0x1a, 0xa2, 0x74, 0x68, 0x12, 0xd2, 0x28, 0xf8, 0xf7, 0xd2,
+	0x4a, 0x75, 0x74, 0xbd, 0xdc, 0x73, 0x38, 0x21, 0x7f, 0xe6, 0x56, 0xbf, 0x00, 0xa5, 0x8d, 0x28,
+	0x46, 0xd1, 0xad, 0xe6, 0x4e, 0xc5, 0x3c, 0x36, 0xec, 0x64, 0x4c, 0x1e, 0x1d, 0x35, 0xa2, 0xf0,
+	0xad, 0xb9, 0x83, 0x9a, 0xb8, 0x64, 0xeb, 0x91, 0x31, 0x82, 0xef, 0x1f, 0xc6, 0x3f, 0x38, 0xea,
+	0xa6, 0x08, 0xe9, 0x71, 0x40, 0xb2, 0x7d, 0x58, 0xa0, 0x30, 0x13, 0x7a, 0x23, 0x7c, 0x31, 0x7a,
+	0x74, 0xb4, 0x4c, 0x56, 0xc9, 0x6e, 0x5a, 0xcd, 0x7f, 0x7b, 0xd5, 0xcf, 0xe5, 0x21, 0x6c, 0x51,
+	0x5a, 0xf8, 0x6b, 0x2f, 0x67, 0x1f, 0xf7, 0xa9, 0xaf, 0x39, 0x4f, 0x1c, 0xf5, 0x9a, 0x0e, 0x65,
+	0xc5, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x00, 0xa0, 0xa8, 0x3f, 0xcf, 0x00, 0x00, 0x00,
+}

+ 10 - 0
transport/internet/tcp/config.proto

@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package v2ray.core.transport.internet.tcp;
+option go_package = "tcp";
+option java_package = "com.v2ray.core.transport.internet.tcp";
+option java_outer_classname = "ConfigProto";
+
+message Config {
+  bool connection_reuse = 1;
+}

+ 5 - 3
transport/internet/tcp/connection.go

@@ -31,14 +31,16 @@ type Connection struct {
 	conn     net.Conn
 	listener ConnectionManager
 	reusable bool
+	config   *Config
 }
 
-func NewConnection(dest string, conn net.Conn, manager ConnectionManager) *Connection {
+func NewConnection(dest string, conn net.Conn, manager ConnectionManager, config *Config) *Connection {
 	return &Connection{
 		dest:     dest,
 		conn:     conn,
 		listener: manager,
-		reusable: effectiveConfig.ConnectionReuse,
+		reusable: config.ConnectionReuse,
+		config:   config,
 	}
 }
 
@@ -91,7 +93,7 @@ func (this *Connection) SetWriteDeadline(t time.Time) error {
 }
 
 func (this *Connection) SetReusable(reusable bool) {
-	if !effectiveConfig.ConnectionReuse {
+	if !this.config.ConnectionReuse {
 		return
 	}
 	this.reusable = reusable

+ 16 - 4
transport/internet/tcp/dialer.go

@@ -7,6 +7,7 @@ import (
 	"v2ray.com/core/common/log"
 	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/transport/internet"
+	v2tls "v2ray.com/core/transport/internet/tls"
 )
 
 var (
@@ -18,9 +19,15 @@ func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOpti
 	if src == nil {
 		src = v2net.AnyIP
 	}
+	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
+	if err != nil {
+		return nil, err
+	}
+	tcpSettings := networkSettings.(*Config)
+
 	id := src.String() + "-" + dest.NetAddr()
 	var conn net.Conn
-	if dest.Network == v2net.Network_TCP && effectiveConfig.ConnectionReuse {
+	if dest.Network == v2net.Network_TCP && tcpSettings.ConnectionReuse {
 		conn = globalCache.Get(id)
 	}
 	if conn == nil {
@@ -30,14 +37,19 @@ func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOpti
 			return nil, err
 		}
 	}
-	if options.Stream != nil && options.Stream.Security == internet.StreamSecurityTypeTLS {
-		config := options.Stream.TLSSettings.GetTLSConfig()
+	if options.Stream != nil && options.Stream.SecurityType == internet.SecurityType_TLS {
+		securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
+		if err != nil {
+			log.Error("TCP: Failed to apply TLS config: ", err)
+			return nil, err
+		}
+		config := securitySettings.(*v2tls.Config).GetTLSConfig()
 		if dest.Address.Family().IsDomain() {
 			config.ServerName = dest.Address.Domain()
 		}
 		conn = tls.Client(conn, config)
 	}
-	return NewConnection(id, conn, globalCache), nil
+	return NewConnection(id, conn, globalCache, tcpSettings), nil
 }
 
 func DialRaw(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (internet.Connection, error) {

+ 18 - 3
transport/internet/tcp/hub.go

@@ -7,8 +7,10 @@ import (
 	"sync"
 	"time"
 
+	"v2ray.com/core/common/log"
 	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/transport/internet"
+	v2tls "v2ray.com/core/transport/internet/tls"
 )
 
 var (
@@ -26,6 +28,7 @@ type TCPListener struct {
 	listener      *net.TCPListener
 	awaitingConns chan *ConnectionWithError
 	tlsConfig     *tls.Config
+	config        *Config
 }
 
 func ListenTCP(address v2net.Address, port v2net.Port, options internet.ListenOptions) (internet.Listener, error) {
@@ -36,13 +39,25 @@ func ListenTCP(address v2net.Address, port v2net.Port, options internet.ListenOp
 	if err != nil {
 		return nil, err
 	}
+	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
+	if err != nil {
+		return nil, err
+	}
+	tcpSettings := networkSettings.(*Config)
+
 	l := &TCPListener{
 		acccepting:    true,
 		listener:      listener,
 		awaitingConns: make(chan *ConnectionWithError, 32),
+		config:        tcpSettings,
 	}
-	if options.Stream != nil && options.Stream.Security == internet.StreamSecurityTypeTLS {
-		l.tlsConfig = options.Stream.TLSSettings.GetTLSConfig()
+	if options.Stream != nil && options.Stream.SecurityType == internet.SecurityType_TLS {
+		securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
+		if err != nil {
+			log.Error("TCP: Failed to apply TLS config: ", err)
+			return nil, err
+		}
+		l.tlsConfig = securitySettings.(*v2tls.Config).GetTLSConfig()
 	}
 	go l.KeepAccepting()
 	return l, nil
@@ -62,7 +77,7 @@ func (this *TCPListener) Accept() (internet.Connection, error) {
 			if this.tlsConfig != nil {
 				conn = tls.Server(conn, this.tlsConfig)
 			}
-			return NewConnection("", conn, this), nil
+			return NewConnection("", conn, this, this.config), nil
 		case <-time.After(time.Second * 2):
 		}
 	}

+ 8 - 8
transport/internet/tcp_hub.go

@@ -20,7 +20,7 @@ var (
 
 type ListenFunc func(address v2net.Address, port v2net.Port, options ListenOptions) (Listener, error)
 type ListenOptions struct {
-	Stream *StreamSettings
+	Stream *StreamConfig
 }
 
 type Listener interface {
@@ -36,23 +36,23 @@ type TCPHub struct {
 	accepting    bool
 }
 
-func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandler, settings *StreamSettings) (*TCPHub, error) {
+func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandler, settings *StreamConfig) (*TCPHub, error) {
 	var listener Listener
 	var err error
 	options := ListenOptions{
 		Stream: settings,
 	}
-	switch {
-	case settings.IsCapableOf(StreamConnectionTypeTCP):
+	switch settings.Network {
+	case v2net.Network_TCP:
 		listener, err = TCPListenFunc(address, port, options)
-	case settings.IsCapableOf(StreamConnectionTypeKCP):
+	case v2net.Network_KCP:
 		listener, err = KCPListenFunc(address, port, options)
-	case settings.IsCapableOf(StreamConnectionTypeWebSocket):
+	case v2net.Network_WebSocket:
 		listener, err = WSListenFunc(address, port, options)
-	case settings.IsCapableOf(StreamConnectionTypeRawTCP):
+	case v2net.Network_RawTCP:
 		listener, err = RawTCPListenFunc(address, port, options)
 	default:
-		log.Error("Internet|Listener: Unknown stream type: ", settings.Type)
+		log.Error("Internet|Listener: Unknown stream type: ", settings.Network)
 		err = ErrUnsupportedStreamType
 	}
 

+ 39 - 0
transport/internet/tls/config.go

@@ -0,0 +1,39 @@
+package tls
+
+import (
+	"crypto/tls"
+
+	"v2ray.com/core/common/log"
+)
+
+var (
+	globalSessionCache = tls.NewLRUClientSessionCache(128)
+)
+
+func (this *Config) BuildCertificates() []tls.Certificate {
+	certs := make([]tls.Certificate, 0, len(this.Certificate))
+	for _, entry := range this.Certificate {
+		keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
+		if err != nil {
+			log.Warning("TLS: ignoring invalid X509 key pair: ", err)
+			continue
+		}
+		certs = append(certs, keyPair)
+	}
+	return certs
+}
+
+func (this *Config) GetTLSConfig() *tls.Config {
+	config := &tls.Config{
+		ClientSessionCache: globalSessionCache,
+	}
+	if this == nil {
+		return config
+	}
+
+	config.InsecureSkipVerify = this.AllowInsecure
+	config.Certificates = this.BuildCertificates()
+	config.BuildNameToCertificate()
+
+	return config
+}

+ 82 - 0
transport/internet/tls/config.pb.go

@@ -0,0 +1,82 @@
+// Code generated by protoc-gen-go.
+// source: v2ray.com/core/transport/internet/tls/config.proto
+// DO NOT EDIT!
+
+/*
+Package tls is a generated protocol buffer package.
+
+It is generated from these files:
+	v2ray.com/core/transport/internet/tls/config.proto
+
+It has these top-level messages:
+	Certificate
+	Config
+*/
+package tls
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Certificate struct {
+	Certificate []byte `protobuf:"bytes,1,opt,name=Certificate,json=certificate,proto3" json:"Certificate,omitempty"`
+	Key         []byte `protobuf:"bytes,2,opt,name=Key,json=key,proto3" json:"Key,omitempty"`
+}
+
+func (m *Certificate) Reset()                    { *m = Certificate{} }
+func (m *Certificate) String() string            { return proto.CompactTextString(m) }
+func (*Certificate) ProtoMessage()               {}
+func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+type Config struct {
+	AllowInsecure bool           `protobuf:"varint,1,opt,name=allow_insecure,json=allowInsecure" json:"allow_insecure,omitempty"`
+	Certificate   []*Certificate `protobuf:"bytes,2,rep,name=certificate" json:"certificate,omitempty"`
+}
+
+func (m *Config) Reset()                    { *m = Config{} }
+func (m *Config) String() string            { return proto.CompactTextString(m) }
+func (*Config) ProtoMessage()               {}
+func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *Config) GetCertificate() []*Certificate {
+	if m != nil {
+		return m.Certificate
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Certificate)(nil), "v2ray.core.transport.internet.tls.Certificate")
+	proto.RegisterType((*Config)(nil), "v2ray.core.transport.internet.tls.Config")
+}
+
+func init() { proto.RegisterFile("v2ray.com/core/transport/internet/tls/config.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+	// 219 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x90, 0xb1, 0x4b, 0x43, 0x31,
+	0x10, 0xc6, 0x79, 0x0d, 0x14, 0x49, 0x54, 0x24, 0xd3, 0x1b, 0x9f, 0x85, 0x42, 0xa7, 0x0b, 0x3c,
+	0x27, 0x47, 0xdb, 0x49, 0x5c, 0x4a, 0x47, 0x17, 0x89, 0xe1, 0x2a, 0xc1, 0x34, 0x57, 0x2e, 0xa7,
+	0xf2, 0x46, 0xff, 0x73, 0x31, 0xb5, 0xf2, 0x3a, 0x75, 0xbc, 0xef, 0xbe, 0xfb, 0xee, 0xc7, 0xa7,
+	0xfb, 0xcf, 0x9e, 0xfd, 0x00, 0x81, 0x76, 0x2e, 0x10, 0xa3, 0x13, 0xf6, 0xb9, 0xec, 0x89, 0xc5,
+	0xc5, 0x2c, 0xc8, 0x19, 0xc5, 0x49, 0x2a, 0x2e, 0x50, 0xde, 0xc6, 0x37, 0xd8, 0x33, 0x09, 0xd9,
+	0xdb, 0xe3, 0x0d, 0x23, 0xfc, 0xfb, 0xe1, 0xe8, 0x07, 0x49, 0x65, 0xf6, 0xa0, 0xcd, 0x0a, 0x59,
+	0xe2, 0x36, 0x06, 0x2f, 0x68, 0xbb, 0x93, 0xb1, 0x6d, 0xba, 0x66, 0x71, 0xb9, 0x31, 0x61, 0xe4,
+	0xb8, 0xd1, 0xea, 0x09, 0x87, 0x76, 0x52, 0x37, 0xea, 0x1d, 0x87, 0xd9, 0x77, 0xa3, 0xa7, 0xab,
+	0xfa, 0xd6, 0xce, 0xf5, 0xb5, 0x4f, 0x89, 0xbe, 0x5e, 0x62, 0x2e, 0x18, 0x3e, 0xf8, 0x90, 0x70,
+	0xb1, 0xb9, 0xaa, 0xea, 0xe3, 0x9f, 0x68, 0xd7, 0x7a, 0x1c, 0xd9, 0x4e, 0x3a, 0xb5, 0x30, 0x3d,
+	0xc0, 0x59, 0x5a, 0x18, 0xb1, 0x9d, 0x50, 0x2d, 0xef, 0xf5, 0x3c, 0xd0, 0xee, 0x7c, 0xc2, 0xd2,
+	0x1c, 0x48, 0xd7, 0xbf, 0xfd, 0x3c, 0x2b, 0x49, 0xe5, 0x75, 0x5a, 0xbb, 0xba, 0xfb, 0x09, 0x00,
+	0x00, 0xff, 0xff, 0xbb, 0x82, 0x9d, 0x49, 0x61, 0x01, 0x00, 0x00,
+}

+ 16 - 0
transport/internet/tls/config.proto

@@ -0,0 +1,16 @@
+syntax = "proto3";
+
+package v2ray.core.transport.internet.tls;
+option go_package = "tls";
+option java_package = "com.v2ray.core.transport.internet.tls";
+option java_outer_classname = "ConfigProto";
+
+message Certificate {
+  bytes Certificate = 1;
+  bytes Key = 2;
+}
+
+message Config {
+  bool allow_insecure = 1;
+  repeated Certificate certificate = 2;
+}

+ 41 - 0
transport/internet/tls/config_json.go

@@ -0,0 +1,41 @@
+// +build json
+
+package tls
+
+import (
+	"encoding/json"
+	"errors"
+	"io/ioutil"
+)
+
+func (this *Config) UnmarshalJSON(data []byte) error {
+	type JSONCertConfig struct {
+		CertFile string `json:"certificateFile"`
+		KeyFile  string `json:"keyFile"`
+	}
+	type JSONConfig struct {
+		Insecure bool              `json:"allowInsecure"`
+		Certs    []*JSONCertConfig `json:"certificates"`
+	}
+	jsonConfig := new(JSONConfig)
+	if err := json.Unmarshal(data, jsonConfig); err != nil {
+		return err
+	}
+	this.Certificate = make([]*Certificate, len(jsonConfig.Certs))
+	for idx, certConf := range jsonConfig.Certs {
+		cert, err := ioutil.ReadFile(certConf.CertFile)
+		if err != nil {
+			return errors.New("TLS: Failed to load certificate file: " + err.Error())
+		}
+		key, err := ioutil.ReadFile(certConf.KeyFile)
+		if err != nil {
+			return errors.New("TLS: Failed to load key file: " + err.Error())
+		}
+		this.Certificate[idx] = &Certificate{
+			Key:         key,
+			Certificate: cert,
+		}
+	}
+	this.AllowInsecure = jsonConfig.Insecure
+	return nil
+}

+ 10 - 13
transport/internet/ws/config.go

@@ -1,17 +1,14 @@
 package ws
 
-type Config struct {
-	ConnectionReuse bool
-	Path            string
-}
-
-func (this *Config) Apply() {
-	effectiveConfig = this
-}
+import (
+	v2net "v2ray.com/core/common/net"
+	"v2ray.com/core/transport/internet"
 
-var (
-	effectiveConfig = &Config{
-		ConnectionReuse: true,
-		Path:            "",
-	}
+	"github.com/golang/protobuf/proto"
 )
+
+func init() {
+	internet.RegisterNetworkConfigCreator(v2net.Network_WebSocket, func() proto.Message {
+		return new(Config)
+	})
+}

+ 60 - 0
transport/internet/ws/config.pb.go

@@ -0,0 +1,60 @@
+// Code generated by protoc-gen-go.
+// source: v2ray.com/core/transport/internet/ws/config.proto
+// DO NOT EDIT!
+
+/*
+Package ws is a generated protocol buffer package.
+
+It is generated from these files:
+	v2ray.com/core/transport/internet/ws/config.proto
+
+It has these top-level messages:
+	Config
+*/
+package ws
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Config struct {
+	ConnectionReuse bool   `protobuf:"varint,1,opt,name=connection_reuse,json=connectionReuse" json:"connection_reuse,omitempty"`
+	Path            string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
+}
+
+func (m *Config) Reset()                    { *m = Config{} }
+func (m *Config) String() string            { return proto.CompactTextString(m) }
+func (*Config) ProtoMessage()               {}
+func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func init() {
+	proto.RegisterType((*Config)(nil), "v2ray.core.transport.internet.ws.Config")
+}
+
+func init() { proto.RegisterFile("v2ray.com/core/transport/internet/ws/config.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+	// 174 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x32, 0x2c, 0x33, 0x2a, 0x4a,
+	0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x2f, 0x29, 0x4a, 0xcc, 0x2b,
+	0x2e, 0xc8, 0x2f, 0x2a, 0xd1, 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0xca, 0x4b, 0x2d, 0xd1, 0x2f, 0x2f,
+	0xd6, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52, 0x80,
+	0x69, 0x29, 0x4a, 0xd5, 0x83, 0x2b, 0xd7, 0x83, 0x29, 0xd7, 0x2b, 0x2f, 0x56, 0x72, 0xe7, 0x62,
+	0x73, 0x06, 0xeb, 0x10, 0xd2, 0xe4, 0x12, 0x48, 0xce, 0xcf, 0xcb, 0x4b, 0x4d, 0x2e, 0xc9, 0xcc,
+	0xcf, 0x8b, 0x2f, 0x4a, 0x2d, 0x2d, 0x4e, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x08, 0xe2, 0x47,
+	0x88, 0x07, 0x81, 0x84, 0x85, 0x84, 0xb8, 0x58, 0x0a, 0x12, 0x4b, 0x32, 0x24, 0x98, 0x14, 0x18,
+	0x35, 0x38, 0x83, 0xc0, 0x6c, 0x27, 0x73, 0x2e, 0x95, 0xe4, 0xfc, 0x5c, 0x3d, 0x42, 0x16, 0x3a,
+	0x71, 0x43, 0xac, 0x0b, 0x00, 0xb9, 0x2f, 0x8a, 0xa9, 0xbc, 0x38, 0x89, 0x0d, 0xec, 0x54, 0x63,
+	0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x69, 0xf5, 0xaf, 0xdf, 0x00, 0x00, 0x00,
+}

+ 11 - 0
transport/internet/ws/config.proto

@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package v2ray.core.transport.internet.ws;
+option go_package = "ws";
+option java_package = "com.v2ray.core.transport.internet.ws";
+option java_outer_classname = "ConfigProto";
+
+message Config {
+  bool connection_reuse = 1;
+  string path = 2;
+}

+ 5 - 3
transport/internet/ws/connection.go

@@ -20,14 +20,16 @@ type Connection struct {
 	conn     *wsconn
 	listener ConnectionManager
 	reusable bool
+	config   *Config
 }
 
-func NewConnection(dest string, conn *wsconn, manager ConnectionManager) *Connection {
+func NewConnection(dest string, conn *wsconn, manager ConnectionManager, config *Config) *Connection {
 	return &Connection{
 		dest:     dest,
 		conn:     conn,
 		listener: manager,
-		reusable: effectiveConfig.ConnectionReuse,
+		reusable: config.ConnectionReuse,
+		config:   config,
 	}
 }
 
@@ -80,7 +82,7 @@ func (this *Connection) SetWriteDeadline(t time.Time) error {
 }
 
 func (this *Connection) SetReusable(reusable bool) {
-	if !effectiveConfig.ConnectionReuse {
+	if !this.config.ConnectionReuse {
 		return
 	}
 	this.reusable = reusable

+ 28 - 6
transport/internet/ws/dialer.go

@@ -9,6 +9,7 @@ import (
 	"v2ray.com/core/common/log"
 	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/transport/internet"
+	v2tls "v2ray.com/core/transport/internet/tls"
 )
 
 var (
@@ -20,9 +21,15 @@ func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOpti
 	if src == nil {
 		src = v2net.AnyIP
 	}
+	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
+	if err != nil {
+		return nil, err
+	}
+	wsSettings := networkSettings.(*Config)
+
 	id := src.String() + "-" + dest.NetAddr()
 	var conn *wsconn
-	if dest.Network == v2net.Network_TCP && effectiveConfig.ConnectionReuse {
+	if dest.Network == v2net.Network_TCP && wsSettings.ConnectionReuse {
 		connt := globalCache.Get(id)
 		if connt != nil {
 			conn = connt.(*wsconn)
@@ -36,7 +43,7 @@ func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOpti
 			return nil, err
 		}
 	}
-	return NewConnection(id, conn, globalCache), nil
+	return NewConnection(id, conn, globalCache, wsSettings), nil
 }
 
 func init() {
@@ -44,6 +51,12 @@ func init() {
 }
 
 func wsDial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (*wsconn, error) {
+	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
+	if err != nil {
+		return nil, err
+	}
+	wsSettings := networkSettings.(*Config)
+
 	commonDial := func(network, addr string) (net.Conn, error) {
 		return internet.DialToDest(src, dest)
 	}
@@ -56,9 +69,14 @@ func wsDial(src v2net.Address, dest v2net.Destination, options internet.DialerOp
 
 	protocol := "ws"
 
-	if options.Stream != nil && options.Stream.Security == internet.StreamSecurityTypeTLS {
+	if options.Stream != nil && options.Stream.SecurityType == internet.SecurityType_TLS {
 		protocol = "wss"
-		dialer.TLSClientConfig = options.Stream.TLSSettings.GetTLSConfig()
+		securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
+		if err != nil {
+			log.Error("WebSocket: Failed to create apply TLS config: ", err)
+			return nil, err
+		}
+		dialer.TLSClientConfig = securitySettings.(*v2tls.Config).GetTLSConfig()
 		if dest.Address.Family().IsDomain() {
 			dialer.TLSClientConfig.ServerName = dest.Address.Domain()
 		}
@@ -66,7 +84,7 @@ func wsDial(src v2net.Address, dest v2net.Destination, options internet.DialerOp
 
 	uri := func(dst v2net.Destination, pto string, path string) string {
 		return fmt.Sprintf("%v://%v/%v", pto, dst.NetAddr(), path)
-	}(dest, protocol, effectiveConfig.Path)
+	}(dest, protocol, wsSettings.Path)
 
 	conn, resp, err := dialer.Dial(uri, nil)
 	if err != nil {
@@ -77,7 +95,11 @@ func wsDial(src v2net.Address, dest v2net.Destination, options internet.DialerOp
 		return nil, err
 	}
 	return func() internet.Connection {
-		connv2ray := &wsconn{wsc: conn, connClosing: false}
+		connv2ray := &wsconn{
+			wsc:         conn,
+			connClosing: false,
+			config:      wsSettings,
+		}
 		connv2ray.setup()
 		return connv2ray
 	}().(*wsconn), nil

+ 18 - 6
transport/internet/ws/hub.go

@@ -13,6 +13,7 @@ import (
 	"v2ray.com/core/common/log"
 	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/transport/internet"
+	v2tls "v2ray.com/core/transport/internet/tls"
 )
 
 var (
@@ -30,26 +31,37 @@ type WSListener struct {
 	awaitingConns chan *ConnectionWithError
 	listener      *StoppableListener
 	tlsConfig     *tls.Config
+	config        *Config
 }
 
 func ListenWS(address v2net.Address, port v2net.Port, options internet.ListenOptions) (internet.Listener, error) {
+	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
+	if err != nil {
+		return nil, err
+	}
+	wsSettings := networkSettings.(*Config)
 
 	l := &WSListener{
 		acccepting:    true,
 		awaitingConns: make(chan *ConnectionWithError, 32),
+		config:        wsSettings,
 	}
-	if options.Stream != nil && options.Stream.Security == internet.StreamSecurityTypeTLS {
-		l.tlsConfig = options.Stream.TLSSettings.GetTLSConfig()
+	if options.Stream != nil && options.Stream.SecurityType == internet.SecurityType_TLS {
+		securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
+		if err != nil {
+			log.Error("WebSocket: Failed to create apply TLS config: ", err)
+			return nil, err
+		}
+		l.tlsConfig = securitySettings.(*v2tls.Config).GetTLSConfig()
 	}
 
-	err := l.listenws(address, port)
+	err = l.listenws(address, port)
 
 	return l, err
 }
 
 func (wsl *WSListener) listenws(address v2net.Address, port v2net.Port) error {
-
-	http.HandleFunc("/"+effectiveConfig.Path, func(w http.ResponseWriter, r *http.Request) {
+	http.HandleFunc("/"+wsl.config.Path, func(w http.ResponseWriter, r *http.Request) {
 		con, err := wsl.converttovws(w, r)
 		if err != nil {
 			log.Warning("WebSocket|Listener: Failed to convert connection: ", err)
@@ -140,7 +152,7 @@ func (this *WSListener) Accept() (internet.Connection, error) {
 			if connErr.err != nil {
 				return nil, connErr.err
 			}
-			return NewConnection("", connErr.conn.(*wsconn), this), nil
+			return NewConnection("", connErr.conn.(*wsconn), this, this.config), nil
 		case <-time.After(time.Second * 2):
 		}
 	}

+ 191 - 39
transport/internet/ws/ws_test.go

@@ -1,20 +1,43 @@
 package ws_test
 
 import (
-	"crypto/tls"
+	"io/ioutil"
 	"testing"
 	"time"
 
 	v2net "v2ray.com/core/common/net"
 	"v2ray.com/core/testing/assert"
 	"v2ray.com/core/transport/internet"
+	v2tls "v2ray.com/core/transport/internet/tls"
 	. "v2ray.com/core/transport/internet/ws"
+
+	"github.com/golang/protobuf/ptypes"
+	"github.com/golang/protobuf/ptypes/any"
 )
 
+func MarshalSettings(config *Config, assert *assert.Assert) *any.Any {
+	anySettings, err := ptypes.MarshalAny(config)
+	assert.Error(err).IsNil()
+
+	return anySettings
+}
+
 func Test_Connect_ws(t *testing.T) {
 	assert := assert.On(t)
-	(&Config{Path: ""}).Apply()
-	conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 80), internet.DialerOptions{})
+
+	conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 80), internet.DialerOptions{
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "",
+					}, assert),
+				},
+			},
+		},
+	})
 	assert.Error(err).IsNil()
 	conn.Write([]byte("echo"))
 	s := make(chan int)
@@ -33,10 +56,18 @@ func Test_Connect_ws(t *testing.T) {
 
 func Test_Connect_wss(t *testing.T) {
 	assert := assert.On(t)
-	(&Config{Path: ""}).Apply()
 	conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 443), internet.DialerOptions{
-		Stream: &internet.StreamSettings{
-			Security: internet.StreamSecurityTypeTLS,
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "",
+					}, assert),
+				},
+			},
+			SecurityType: internet.SecurityType_TLS,
 		},
 	})
 	assert.Error(err).IsNil()
@@ -57,10 +88,18 @@ func Test_Connect_wss(t *testing.T) {
 
 func Test_Connect_wss_1_nil(t *testing.T) {
 	assert := assert.On(t)
-	(&Config{Path: ""}).Apply()
 	conn, err := Dial(nil, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 443), internet.DialerOptions{
-		Stream: &internet.StreamSettings{
-			Security: internet.StreamSecurityTypeTLS,
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "",
+					}, assert),
+				},
+			},
+			SecurityType: internet.SecurityType_TLS,
 		},
 	})
 	assert.Error(err).IsNil()
@@ -81,8 +120,19 @@ func Test_Connect_wss_1_nil(t *testing.T) {
 
 func Test_Connect_ws_guess(t *testing.T) {
 	assert := assert.On(t)
-	(&Config{Path: ""}).Apply()
-	conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 80), internet.DialerOptions{})
+	conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 80), internet.DialerOptions{
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "",
+					}, assert),
+				},
+			},
+		},
+	})
 	assert.Error(err).IsNil()
 	conn.Write([]byte("echo"))
 	s := make(chan int)
@@ -101,10 +151,18 @@ func Test_Connect_ws_guess(t *testing.T) {
 
 func Test_Connect_wss_guess(t *testing.T) {
 	assert := assert.On(t)
-	(&Config{Path: ""}).Apply()
 	conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 443), internet.DialerOptions{
-		Stream: &internet.StreamSettings{
-			Security: internet.StreamSecurityTypeTLS,
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "",
+					}, assert),
+				},
+			},
+			SecurityType: internet.SecurityType_TLS,
 		},
 	})
 	assert.Error(err).IsNil()
@@ -125,10 +183,18 @@ func Test_Connect_wss_guess(t *testing.T) {
 
 func Test_Connect_wss_guess_fail(t *testing.T) {
 	assert := assert.On(t)
-	(&Config{Path: ""}).Apply()
 	_, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("static.kkdev.org"), 443), internet.DialerOptions{
-		Stream: &internet.StreamSettings{
-			Security: internet.StreamSecurityTypeTLS,
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "",
+					}, assert),
+				},
+			},
+			SecurityType: internet.SecurityType_TLS,
 		},
 	})
 	assert.Error(err).IsNotNil()
@@ -136,12 +202,21 @@ func Test_Connect_wss_guess_fail(t *testing.T) {
 
 func Test_Connect_wss_guess_reuse(t *testing.T) {
 	assert := assert.On(t)
-	(&Config{Path: "", ConnectionReuse: true}).Apply()
 	i := 3
 	for i != 0 {
 		conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 443), internet.DialerOptions{
-			Stream: &internet.StreamSettings{
-				Security: internet.StreamSecurityTypeTLS,
+			Stream: &internet.StreamConfig{
+				Network: v2net.Network_WebSocket,
+				NetworkSettings: []*internet.NetworkSettings{
+					{
+						Network: v2net.Network_WebSocket,
+						Settings: MarshalSettings(&Config{
+							Path:            "",
+							ConnectionReuse: true,
+						}, assert),
+					},
+				},
+				SecurityType: internet.SecurityType_TLS,
 			},
 		})
 		assert.Error(err).IsNil()
@@ -170,8 +245,19 @@ func Test_Connect_wss_guess_reuse(t *testing.T) {
 
 func Test_listenWSAndDial(t *testing.T) {
 	assert := assert.On(t)
-	(&Config{Path: "ws"}).Apply()
-	listen, err := ListenWS(v2net.DomainAddress("localhost"), 13142, internet.ListenOptions{})
+	listen, err := ListenWS(v2net.DomainAddress("localhost"), 13142, internet.ListenOptions{
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "ws",
+					}, assert),
+				},
+			},
+		},
+	})
 	assert.Error(err).IsNil()
 	go func() {
 		conn, err := listen.Accept()
@@ -185,15 +271,51 @@ func Test_listenWSAndDial(t *testing.T) {
 		conn.Close()
 		listen.Close()
 	}()
-	conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13142), internet.DialerOptions{})
+	conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13142), internet.DialerOptions{
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "ws",
+					}, assert),
+				},
+			},
+		},
+	})
 	assert.Error(err).IsNil()
 	conn.Close()
 	<-time.After(time.Second * 5)
-	conn, err = Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13142), internet.DialerOptions{})
+	conn, err = Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13142), internet.DialerOptions{
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "ws",
+					}, assert),
+				},
+			},
+		},
+	})
 	assert.Error(err).IsNil()
 	conn.Close()
 	<-time.After(time.Second * 15)
-	conn, err = Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13142), internet.DialerOptions{})
+	conn, err = Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13142), internet.DialerOptions{
+		Stream: &internet.StreamConfig{
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path: "ws",
+					}, assert),
+				},
+			},
+		},
+	})
 	assert.Error(err).IsNil()
 	conn.Close()
 }
@@ -204,14 +326,34 @@ func Test_listenWSAndDial_TLS(t *testing.T) {
 		<-time.After(time.Second * 5)
 		assert.Fail("Too slow")
 	}()
-	(&Config{Path: "wss", ConnectionReuse: true}).Apply()
+	tlsSettings := &v2tls.Config{
+		AllowInsecure: true,
+		Certificate: []*v2tls.Certificate{
+			{
+				Certificate: ReadFile("./../../../testing/tls/cert.pem", assert),
+				Key:         ReadFile("./../../../testing/tls/key.pem", assert),
+			},
+		},
+	}
+	anyTlsSettings, err := ptypes.MarshalAny(tlsSettings)
+	assert.Error(err).IsNil()
 
 	listen, err := ListenWS(v2net.DomainAddress("localhost"), 13143, internet.ListenOptions{
-		Stream: &internet.StreamSettings{
-			Security: internet.StreamSecurityTypeTLS,
-			TLSSettings: &internet.TLSSettings{
-				AllowInsecure: true,
-				Certs:         LoadTestCert(assert),
+		Stream: &internet.StreamConfig{
+			SecurityType: internet.SecurityType_TLS,
+			SecuritySettings: []*internet.SecuritySettings{{
+				Type:     internet.SecurityType_TLS,
+				Settings: anyTlsSettings,
+			}},
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path:            "wss",
+						ConnectionReuse: true,
+					}, assert),
+				},
 			},
 		},
 	})
@@ -223,11 +365,21 @@ func Test_listenWSAndDial_TLS(t *testing.T) {
 		listen.Close()
 	}()
 	conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13143), internet.DialerOptions{
-		Stream: &internet.StreamSettings{
-			Security: internet.StreamSecurityTypeTLS,
-			TLSSettings: &internet.TLSSettings{
-				AllowInsecure: true,
-				Certs:         LoadTestCert(assert),
+		Stream: &internet.StreamConfig{
+			SecurityType: internet.SecurityType_TLS,
+			SecuritySettings: []*internet.SecuritySettings{{
+				Type:     internet.SecurityType_TLS,
+				Settings: anyTlsSettings,
+			}},
+			Network: v2net.Network_WebSocket,
+			NetworkSettings: []*internet.NetworkSettings{
+				{
+					Network: v2net.Network_WebSocket,
+					Settings: MarshalSettings(&Config{
+						Path:            "wss",
+						ConnectionReuse: true,
+					}, assert),
+				},
 			},
 		},
 	})
@@ -235,8 +387,8 @@ func Test_listenWSAndDial_TLS(t *testing.T) {
 	conn.Close()
 }
 
-func LoadTestCert(assert *assert.Assert) []tls.Certificate {
-	cert, err := tls.LoadX509KeyPair("./../../../testing/tls/cert.pem", "./../../../testing/tls/key.pem")
+func ReadFile(file string, assert *assert.Assert) []byte {
+	b, err := ioutil.ReadFile(file)
 	assert.Error(err).IsNil()
-	return []tls.Certificate{cert}
+	return b
 }

+ 2 - 1
transport/internet/ws/wsconn.go

@@ -18,6 +18,7 @@ type wsconn struct {
 	reusable    bool
 	rlock       *sync.Mutex
 	wlock       *sync.Mutex
+	config      *Config
 }
 
 func (ws *wsconn) Read(b []byte) (n int, err error) {
@@ -164,7 +165,7 @@ func (ws *wsconn) Reusable() bool {
 }
 
 func (ws *wsconn) SetReusable(reusable bool) {
-	if !effectiveConfig.ConnectionReuse {
+	if !ws.config.ConnectionReuse {
 		return
 	}
 	ws.reusable = reusable