From 1d13f47f9ce6e418b8dc157645670113183b9a65 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 2 Oct 2016 23:43:58 +0200 Subject: [PATCH] protobuf for stream settings --- app/dns/server_test.go | 4 +- common/net/network.go | 6 +- common/net/network_json.go | 9 + common/net/network_json_test.go | 9 + proxy/blackhole/blackhole.go | 7 +- proxy/dokodemo/dokodemo.go | 6 +- proxy/dokodemo/dokodemo_test.go | 16 +- proxy/freedom/freedom.go | 6 +- proxy/freedom/freedom_test.go | 12 +- proxy/http/server.go | 6 +- proxy/http/server_test.go | 4 +- proxy/proxy.go | 4 +- proxy/registry/creator.go | 6 +- proxy/registry/handler_cache.go | 18 +- proxy/shadowsocks/server.go | 6 +- proxy/socks/server.go | 6 +- proxy/vmess/inbound/inbound.go | 6 +- proxy/vmess/outbound/outbound.go | 6 +- shell/point/config.go | 8 +- shell/point/config_json.go | 32 +-- transport/config.go | 19 +- transport/config.pb.go | 67 +++++++ transport/config.proto | 4 +- transport/config_json.go | 42 +++- transport/internet/authenticator.pb.go | 4 + transport/internet/config.go | 108 ++++++++++ transport/internet/config.pb.go | 132 +++++++++++++ transport/internet/config.proto | 31 +++ transport/internet/connection.go | 51 ----- transport/internet/connection_json.go | 63 ++---- transport/internet/dialer.go | 14 +- transport/internet/kcp/config.go | 15 +- transport/internet/kcp/connection.go | 13 +- transport/internet/kcp/connection_test.go | 6 +- transport/internet/kcp/dialer.go | 20 +- transport/internet/kcp/kcp_test.go | 30 ++- transport/internet/kcp/listener.go | 24 ++- transport/internet/kcp/output.go | 3 +- transport/internet/kcp/receiving.go | 4 +- transport/internet/kcp/sending.go | 14 +- transport/internet/tcp/config.go | 21 +- transport/internet/tcp/config.pb.go | 58 ++++++ transport/internet/tcp/config.proto | 10 + transport/internet/tcp/connection.go | 8 +- transport/internet/tcp/dialer.go | 20 +- transport/internet/tcp/hub.go | 21 +- transport/internet/tcp_hub.go | 16 +- transport/internet/tls/config.go | 39 ++++ transport/internet/tls/config.pb.go | 82 ++++++++ transport/internet/tls/config.proto | 16 ++ transport/internet/tls/config_json.go | 41 ++++ transport/internet/ws/config.go | 23 +-- transport/internet/ws/config.pb.go | 60 ++++++ transport/internet/ws/config.proto | 11 ++ transport/internet/ws/connection.go | 8 +- transport/internet/ws/dialer.go | 34 +++- transport/internet/ws/hub.go | 24 ++- transport/internet/ws/ws_test.go | 230 ++++++++++++++++++---- transport/internet/ws/wsconn.go | 3 +- 59 files changed, 1243 insertions(+), 323 deletions(-) create mode 100644 transport/config.pb.go create mode 100644 transport/internet/config.go create mode 100644 transport/internet/config.pb.go create mode 100644 transport/internet/config.proto create mode 100644 transport/internet/tcp/config.pb.go create mode 100644 transport/internet/tcp/config.proto create mode 100644 transport/internet/tls/config.go create mode 100644 transport/internet/tls/config.pb.go create mode 100644 transport/internet/tls/config.proto create mode 100644 transport/internet/tls/config_json.go create mode 100644 transport/internet/ws/config.pb.go create mode 100644 transport/internet/ws/config.proto diff --git a/app/dns/server_test.go b/app/dns/server_test.go index b8e7a6b41..2bf446130 100644 --- a/app/dns/server_test.go +++ b/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) diff --git a/common/net/network.go b/common/net/network.go index cee0a006d..8d13c2330 100644 --- a/common/net/network.go +++ b/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] +} diff --git a/common/net/network_json.go b/common/net/network_json.go index 8eb8e0b3a..93e994765 100644 --- a/common/net/network_json.go +++ b/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 { diff --git a/common/net/network_json_test.go b/common/net/network_json_test.go index d4f192d5d..2bcbd6ecb 100644 --- a/common/net/network_json_test.go +++ b/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) diff --git a/proxy/blackhole/blackhole.go b/proxy/blackhole/blackhole.go index 58796e0e0..bacd91e26 100644 --- a/proxy/blackhole/blackhole.go +++ b/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) { diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index d7422392f..dea9e1fef 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/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) { diff --git a/proxy/dokodemo/dokodemo_test.go b/proxy/dokodemo/dokodemo_test.go index 07d463143..4e34ed5c6 100644 --- a/proxy/dokodemo/dokodemo_test.go +++ b/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() diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 99ad2d7d8..9e517c6d0 100644 --- a/proxy/freedom/freedom.go +++ b/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) { diff --git a/proxy/freedom/freedom_test.go b/proxy/freedom/freedom_test.go index 68aa52e2e..13a4b6f10 100644 --- a/proxy/freedom/freedom_test.go +++ b/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, }, }) diff --git a/proxy/http/server.go b/proxy/http/server.go index 7e9a870ef..1890d6e37 100644 --- a/proxy/http/server.go +++ b/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) { diff --git a/proxy/http/server_test.go b/proxy/http/server_test.go index 3aedc2dcc..d7fcd1fa1 100644 --- a/proxy/http/server_test.go +++ b/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() diff --git a/proxy/proxy.go b/proxy/proxy.go index 28a17de77..026fbea7c 100644 --- a/proxy/proxy.go +++ b/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. diff --git a/proxy/registry/creator.go b/proxy/registry/creator.go index 0d1936298..3ddb35161 100644 --- a/proxy/registry/creator.go +++ b/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) } diff --git a/proxy/registry/handler_cache.go b/proxy/registry/handler_cache.go index c8598c4f9..f042bf52b 100644 --- a/proxy/registry/handler_cache.go +++ b/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 { diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 4f2c36ed9..93afcf861 100644 --- a/proxy/shadowsocks/server.go +++ b/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) { diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 6a0f07813..4afddbe21 100644 --- a/proxy/socks/server.go +++ b/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) { diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 7b449cdda..78dab0856 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/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) { diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index 17ae595b3..b965b79d7 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/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) { diff --git a/shell/point/config.go b/shell/point/config.go index 1b0f0f550..7c047d5bf 100644 --- a/shell/point/config.go +++ b/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 } diff --git a/shell/point/config_json.go b/shell/point/config_json.go index 848c7dcc6..739db1748 100644 --- a/shell/point/config_json.go +++ b/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 { diff --git a/transport/config.go b/transport/config.go index dde8a7918..c5349908f 100644 --- a/transport/config.go +++ b/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 } diff --git a/transport/config.pb.go b/transport/config.pb.go new file mode 100644 index 000000000..7e511830a --- /dev/null +++ b/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, +} diff --git a/transport/config.proto b/transport/config.proto index 4b4da4b50..b4467c01a 100644 --- a/transport/config.proto +++ b/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; } \ No newline at end of file diff --git a/transport/config_json.go b/transport/config_json.go index beb103d26..af583b4db 100644 --- a/transport/config_json.go +++ b/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 } diff --git a/transport/internet/authenticator.pb.go b/transport/internet/authenticator.pb.go index c504e821b..fd99bef43 100644 --- a/transport/internet/authenticator.pb.go +++ b/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 diff --git a/transport/internet/config.go b/transport/internet/config.go new file mode 100644 index 000000000..a2c40ae93 --- /dev/null +++ b/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) + }) +} diff --git a/transport/internet/config.pb.go b/transport/internet/config.pb.go new file mode 100644 index 000000000..80a80ee91 --- /dev/null +++ b/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, +} diff --git a/transport/internet/config.proto b/transport/internet/config.proto new file mode 100644 index 000000000..23bc56c7c --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/transport/internet/connection.go b/transport/internet/connection.go index df505f032..f0f33cc15 100644 --- a/transport/internet/connection.go +++ b/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 diff --git a/transport/internet/connection_json.go b/transport/internet/connection_json.go index f21901f22..0de847461 100644 --- a/transport/internet/connection_json.go +++ b/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 + if jsonConfig.Network != nil { + this.Network = *jsonConfig.Network } - 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"` - } - 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 } diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index e6b6e5a76..c1f07f2b7 100644 --- a/transport/internet/dialer.go +++ b/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 diff --git a/transport/internet/kcp/config.go b/transport/internet/kcp/config.go index 66368bb2f..26eaf54e0 100644 --- a/transport/internet/kcp/config.go +++ b/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) + }) +} diff --git a/transport/internet/kcp/connection.go b/transport/internet/kcp/connection.go index 5f74cf455..8e0bde83c 100644 --- a/transport/internet/kcp/connection.go +++ b/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 } diff --git a/transport/internet/kcp/connection_test.go b/transport/internet/kcp/connection_test.go index fd7f51d09..f3250326f 100644 --- a/transport/internet/kcp/connection_test.go +++ b/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 diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go index 93978ff51..be496dee8 100644 --- a/transport/internet/kcp/dialer.go +++ b/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() } diff --git a/transport/internet/kcp/kcp_test.go b/transport/internet/kcp/kcp_test.go index cadd3f75c..1fc712dfa 100644 --- a/transport/internet/kcp/kcp_test.go +++ b/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) diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index 7c47aa066..c60f1fedb 100644 --- a/transport/internet/kcp/listener.go +++ b/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): diff --git a/transport/internet/kcp/output.go b/transport/internet/kcp/output.go index 03aa17c43..001588865 100644 --- a/transport/internet/kcp/output.go +++ b/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()) } diff --git a/transport/internet/kcp/receiving.go b/transport/internet/kcp/receiving.go index 198e432b7..ebc248732 100644 --- a/transport/internet/kcp/receiving.go +++ b/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 diff --git a/transport/internet/kcp/sending.go b/transport/internet/kcp/sending.go index 6d50cc03f..b324314b7 100644 --- a/transport/internet/kcp/sending.go +++ b/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 } diff --git a/transport/internet/tcp/config.go b/transport/internet/tcp/config.go index 6bc1f4d67..2360f989d 100644 --- a/transport/internet/tcp/config.go +++ b/transport/internet/tcp/config.go @@ -1,15 +1,14 @@ package tcp -type Config struct { - ConnectionReuse bool -} +import ( + v2net "v2ray.com/core/common/net" + "v2ray.com/core/transport/internet" -func (this *Config) Apply() { - effectiveConfig = this -} - -var ( - effectiveConfig = &Config{ - ConnectionReuse: true, - } + "github.com/golang/protobuf/proto" ) + +func init() { + internet.RegisterNetworkConfigCreator(v2net.Network_TCP, func() proto.Message { + return new(Config) + }) +} diff --git a/transport/internet/tcp/config.pb.go b/transport/internet/tcp/config.pb.go new file mode 100644 index 000000000..f65373ecf --- /dev/null +++ b/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, +} diff --git a/transport/internet/tcp/config.proto b/transport/internet/tcp/config.proto new file mode 100644 index 000000000..07706ba30 --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/transport/internet/tcp/connection.go b/transport/internet/tcp/connection.go index 2f6447f29..b14e6bd61 100644 --- a/transport/internet/tcp/connection.go +++ b/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 diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index c6ae7edf3..2bd247b98 100644 --- a/transport/internet/tcp/dialer.go +++ b/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) { diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index c0f4da678..21d6033f1 100644 --- a/transport/internet/tcp/hub.go +++ b/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): } } diff --git a/transport/internet/tcp_hub.go b/transport/internet/tcp_hub.go index e771671d9..c579740de 100644 --- a/transport/internet/tcp_hub.go +++ b/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 } diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go new file mode 100644 index 000000000..e40b2e9b5 --- /dev/null +++ b/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 +} diff --git a/transport/internet/tls/config.pb.go b/transport/internet/tls/config.pb.go new file mode 100644 index 000000000..111e2b815 --- /dev/null +++ b/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, +} diff --git a/transport/internet/tls/config.proto b/transport/internet/tls/config.proto new file mode 100644 index 000000000..18052fc2a --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/transport/internet/tls/config_json.go b/transport/internet/tls/config_json.go new file mode 100644 index 000000000..0f582ebf3 --- /dev/null +++ b/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 +} diff --git a/transport/internet/ws/config.go b/transport/internet/ws/config.go index ae0bdf677..130437069 100644 --- a/transport/internet/ws/config.go +++ b/transport/internet/ws/config.go @@ -1,17 +1,14 @@ package ws -type Config struct { - ConnectionReuse bool - Path string -} +import ( + v2net "v2ray.com/core/common/net" + "v2ray.com/core/transport/internet" -func (this *Config) Apply() { - effectiveConfig = this -} - -var ( - effectiveConfig = &Config{ - ConnectionReuse: true, - Path: "", - } + "github.com/golang/protobuf/proto" ) + +func init() { + internet.RegisterNetworkConfigCreator(v2net.Network_WebSocket, func() proto.Message { + return new(Config) + }) +} diff --git a/transport/internet/ws/config.pb.go b/transport/internet/ws/config.pb.go new file mode 100644 index 000000000..9bee7a0ab --- /dev/null +++ b/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, +} diff --git a/transport/internet/ws/config.proto b/transport/internet/ws/config.proto new file mode 100644 index 000000000..b77b2fd81 --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/transport/internet/ws/connection.go b/transport/internet/ws/connection.go index 2ba96df4f..685b393ba 100644 --- a/transport/internet/ws/connection.go +++ b/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 diff --git a/transport/internet/ws/dialer.go b/transport/internet/ws/dialer.go index 5fa65f760..dce8eaa8e 100644 --- a/transport/internet/ws/dialer.go +++ b/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 diff --git a/transport/internet/ws/hub.go b/transport/internet/ws/hub.go index 4791117f2..c5c5495d9 100644 --- a/transport/internet/ws/hub.go +++ b/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): } } diff --git a/transport/internet/ws/ws_test.go b/transport/internet/ws/ws_test.go index b33ea90c4..3592dd816 100644 --- a/transport/internet/ws/ws_test.go +++ b/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 } diff --git a/transport/internet/ws/wsconn.go b/transport/internet/ws/wsconn.go index b32f35ba9..5a4649265 100644 --- a/transport/internet/ws/wsconn.go +++ b/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