瀏覽代碼

customizable policy

Darien Raymond 8 年之前
父節點
當前提交
3214a5078c

+ 3 - 0
app/dns/server/server_test.go

@@ -9,6 +9,8 @@ import (
 	_ "v2ray.com/core/app/dispatcher/impl"
 	. "v2ray.com/core/app/dns"
 	_ "v2ray.com/core/app/dns/server"
+	"v2ray.com/core/app/policy"
+	_ "v2ray.com/core/app/policy/manager"
 	"v2ray.com/core/app/proxyman"
 	_ "v2ray.com/core/app/proxyman/outbound"
 	"v2ray.com/core/common"
@@ -74,6 +76,7 @@ func TestUDPServer(t *testing.T) {
 	common.Must(app.AddApplicationToSpace(ctx, config))
 	common.Must(app.AddApplicationToSpace(ctx, &dispatcher.Config{}))
 	common.Must(app.AddApplicationToSpace(ctx, &proxyman.OutboundConfig{}))
+	common.Must(app.AddApplicationToSpace(ctx, &policy.Config{}))
 
 	om := proxyman.OutboundHandlerManagerFromSpace(space)
 	om.AddHandler(ctx, &proxyman.OutboundHandlerConfig{

+ 15 - 0
app/policy/config.go

@@ -0,0 +1,15 @@
+package policy
+
+import (
+	"time"
+
+	"github.com/golang/protobuf/proto"
+)
+
+func (s *Second) Duration() time.Duration {
+	return time.Second * time.Duration(s.Value)
+}
+
+func (p *Policy) OverrideWith(another *Policy) {
+	proto.Merge(p, another)
+}

+ 140 - 0
app/policy/config.pb.go

@@ -0,0 +1,140 @@
+package policy
+
+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 Second struct {
+	Value uint32 `protobuf:"varint,1,opt,name=value" json:"value,omitempty"`
+}
+
+func (m *Second) Reset()                    { *m = Second{} }
+func (m *Second) String() string            { return proto.CompactTextString(m) }
+func (*Second) ProtoMessage()               {}
+func (*Second) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (m *Second) GetValue() uint32 {
+	if m != nil {
+		return m.Value
+	}
+	return 0
+}
+
+type Policy struct {
+	Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout" json:"timeout,omitempty"`
+}
+
+func (m *Policy) Reset()                    { *m = Policy{} }
+func (m *Policy) String() string            { return proto.CompactTextString(m) }
+func (*Policy) ProtoMessage()               {}
+func (*Policy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *Policy) GetTimeout() *Policy_Timeout {
+	if m != nil {
+		return m.Timeout
+	}
+	return nil
+}
+
+// Timeout is a message for timeout settings in various stages, in seconds.
+type Policy_Timeout struct {
+	Handshake      *Second `protobuf:"bytes,1,opt,name=handshake" json:"handshake,omitempty"`
+	ConnectionIdle *Second `protobuf:"bytes,2,opt,name=connection_idle,json=connectionIdle" json:"connection_idle,omitempty"`
+	UplinkOnly     *Second `protobuf:"bytes,3,opt,name=uplink_only,json=uplinkOnly" json:"uplink_only,omitempty"`
+	DownlinkOnly   *Second `protobuf:"bytes,4,opt,name=downlink_only,json=downlinkOnly" json:"downlink_only,omitempty"`
+}
+
+func (m *Policy_Timeout) Reset()                    { *m = Policy_Timeout{} }
+func (m *Policy_Timeout) String() string            { return proto.CompactTextString(m) }
+func (*Policy_Timeout) ProtoMessage()               {}
+func (*Policy_Timeout) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} }
+
+func (m *Policy_Timeout) GetHandshake() *Second {
+	if m != nil {
+		return m.Handshake
+	}
+	return nil
+}
+
+func (m *Policy_Timeout) GetConnectionIdle() *Second {
+	if m != nil {
+		return m.ConnectionIdle
+	}
+	return nil
+}
+
+func (m *Policy_Timeout) GetUplinkOnly() *Second {
+	if m != nil {
+		return m.UplinkOnly
+	}
+	return nil
+}
+
+func (m *Policy_Timeout) GetDownlinkOnly() *Second {
+	if m != nil {
+		return m.DownlinkOnly
+	}
+	return nil
+}
+
+type Config struct {
+	Level map[uint32]*Policy `protobuf:"bytes,1,rep,name=level" json:"level,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+}
+
+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{2} }
+
+func (m *Config) GetLevel() map[uint32]*Policy {
+	if m != nil {
+		return m.Level
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Second)(nil), "v2ray.core.app.policy.Second")
+	proto.RegisterType((*Policy)(nil), "v2ray.core.app.policy.Policy")
+	proto.RegisterType((*Policy_Timeout)(nil), "v2ray.core.app.policy.Policy.Timeout")
+	proto.RegisterType((*Config)(nil), "v2ray.core.app.policy.Config")
+}
+
+func init() { proto.RegisterFile("v2ray.com/core/app/policy/config.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+	// 349 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x4a, 0xeb, 0x40,
+	0x14, 0x86, 0x49, 0x7a, 0x9b, 0x72, 0x4f, 0x6f, 0xaf, 0x32, 0x58, 0x88, 0x05, 0xa5, 0x14, 0x94,
+	0xae, 0x26, 0x90, 0x6e, 0x44, 0xb1, 0x62, 0x45, 0x41, 0x10, 0x2c, 0x51, 0x14, 0xdc, 0x94, 0x71,
+	0x32, 0xda, 0xd0, 0xe9, 0x9c, 0x21, 0xa6, 0x95, 0xbc, 0x86, 0x6f, 0xe0, 0xd6, 0x87, 0xf2, 0x59,
+	0x24, 0x99, 0x84, 0x6c, 0x5a, 0xe9, 0x6e, 0x72, 0xf8, 0xfe, 0x8f, 0x43, 0xfe, 0x03, 0x87, 0x4b,
+	0x3f, 0x66, 0x29, 0xe5, 0x38, 0xf7, 0x38, 0xc6, 0xc2, 0x63, 0x5a, 0x7b, 0x1a, 0x65, 0xc4, 0x53,
+	0x8f, 0xa3, 0x7a, 0x89, 0x5e, 0xa9, 0x8e, 0x31, 0x41, 0xd2, 0x2e, 0xb9, 0x58, 0x50, 0xa6, 0x35,
+	0x35, 0x4c, 0x6f, 0x1f, 0x9c, 0x3b, 0xc1, 0x51, 0x85, 0x64, 0x07, 0xea, 0x4b, 0x26, 0x17, 0xc2,
+	0xb5, 0xba, 0x56, 0xbf, 0x15, 0x98, 0x8f, 0xde, 0xb7, 0x0d, 0xce, 0x38, 0x47, 0xc9, 0x19, 0x34,
+	0x92, 0x68, 0x2e, 0x70, 0x91, 0xe4, 0x48, 0xd3, 0x3f, 0xa0, 0x2b, 0x9d, 0xd4, 0xf0, 0xf4, 0xde,
+	0xc0, 0x41, 0x99, 0xea, 0x7c, 0xd8, 0xd0, 0x28, 0x86, 0xe4, 0x04, 0xfe, 0x4e, 0x99, 0x0a, 0xdf,
+	0xa6, 0x6c, 0x26, 0x0a, 0xdd, 0xde, 0x1a, 0x9d, 0xd9, 0x2f, 0xa8, 0x78, 0x72, 0x05, 0x5b, 0x1c,
+	0x95, 0x12, 0x3c, 0x89, 0x50, 0x4d, 0xa2, 0x50, 0x0a, 0xd7, 0xde, 0x44, 0xf1, 0xbf, 0x4a, 0x5d,
+	0x87, 0x52, 0x90, 0x21, 0x34, 0x17, 0x5a, 0x46, 0x6a, 0x36, 0x41, 0x25, 0x53, 0xb7, 0xb6, 0x89,
+	0x03, 0x4c, 0xe2, 0x56, 0xc9, 0x94, 0x8c, 0xa0, 0x15, 0xe2, 0xbb, 0xaa, 0x0c, 0x7f, 0x36, 0x31,
+	0xfc, 0x2b, 0x33, 0x99, 0xa3, 0xf7, 0x69, 0x81, 0x73, 0x91, 0x17, 0x45, 0x86, 0x50, 0x97, 0x62,
+	0x29, 0xa4, 0x6b, 0x75, 0x6b, 0xfd, 0xa6, 0xdf, 0x5f, 0xa3, 0x31, 0x34, 0xbd, 0xc9, 0xd0, 0x4b,
+	0x95, 0xc4, 0x69, 0x60, 0x62, 0x9d, 0x47, 0x80, 0x6a, 0x48, 0xb6, 0xa1, 0x36, 0x13, 0x69, 0xd1,
+	0x66, 0xf6, 0x24, 0x83, 0xb2, 0xe1, 0xdf, 0x7f, 0x96, 0xa9, 0xaf, 0x38, 0x80, 0x63, 0xfb, 0xc8,
+	0x1a, 0x9d, 0xc2, 0x2e, 0xc7, 0xf9, 0x6a, 0x7c, 0x6c, 0x3d, 0x39, 0xe6, 0xf5, 0x65, 0xb7, 0x1f,
+	0xfc, 0x80, 0x65, 0x0b, 0xc6, 0x82, 0x9e, 0x6b, 0x5d, 0x98, 0x9e, 0x9d, 0xfc, 0x02, 0x07, 0x3f,
+	0x01, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x25, 0x25, 0xc2, 0xab, 0x02, 0x00, 0x00,
+}

+ 27 - 0
app/policy/config.proto

@@ -0,0 +1,27 @@
+syntax = "proto3";
+
+package v2ray.core.app.policy;
+option csharp_namespace = "V2Ray.Core.App.Policy";
+option go_package = "policy";
+option java_package = "com.v2ray.core.app.policy";
+option java_multiple_files = true;
+
+message Second {
+  uint32 value = 1;
+}
+
+message Policy {
+  // Timeout is a message for timeout settings in various stages, in seconds.
+  message Timeout {
+    Second handshake = 1;
+    Second connection_idle = 2;
+    Second uplink_only = 3;
+    Second downlink_only = 4;
+  }
+
+  Timeout timeout = 1;
+}
+
+message Config {
+  map<uint32, Policy> level = 1;
+}

+ 62 - 0
app/policy/manager/manager.go

@@ -0,0 +1,62 @@
+package manager
+
+import (
+	"context"
+
+	"v2ray.com/core/app/policy"
+	"v2ray.com/core/common"
+)
+
+type Instance struct {
+	levels map[uint32]*policy.Policy
+}
+
+func New(ctx context.Context, config *policy.Config) (*Instance, error) {
+	levels := config.Level
+	if levels == nil {
+		levels = make(map[uint32]*policy.Policy)
+	}
+	for _, p := range levels {
+		g := global()
+		g.OverrideWith(p)
+		*p = g
+	}
+	return &Instance{
+		levels: levels,
+	}, nil
+}
+
+func global() policy.Policy {
+	return policy.Policy{
+		Timeout: &policy.Policy_Timeout{
+			Handshake:      &policy.Second{Value: 4},
+			ConnectionIdle: &policy.Second{Value: 300},
+			UplinkOnly:     &policy.Second{Value: 5},
+			DownlinkOnly:   &policy.Second{Value: 30},
+		},
+	}
+}
+
+func (m *Instance) GetPolicy(level uint32) policy.Policy {
+	if p, ok := m.levels[level]; ok {
+		return *p
+	}
+	return global()
+}
+
+func (m *Instance) Start() error {
+	return nil
+}
+
+func (m *Instance) Close() {
+}
+
+func (m *Instance) Interface() interface{} {
+	return (*policy.Interface)(nil)
+}
+
+func init() {
+	common.Must(common.RegisterConfig((*policy.Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
+		return New(ctx, config.(*policy.Config))
+	}))
+}

+ 17 - 0
app/policy/policy.go

@@ -0,0 +1,17 @@
+package policy
+
+import (
+	"v2ray.com/core/app"
+)
+
+type Interface interface {
+	GetPolicy(level uint32) Policy
+}
+
+func PolicyFromSpace(space app.Space) Interface {
+	app := space.GetApplication((*Interface)(nil))
+	if app == nil {
+		return nil
+	}
+	return app.(Interface)
+}

+ 0 - 19
common/protocol/user.go

@@ -1,7 +1,5 @@
 package protocol
 
-import "time"
-
 func (u *User) GetTypedAccount() (Account, error) {
 	if u.GetAccount() == nil {
 		return nil, newError("Account missing").AtWarning()
@@ -19,20 +17,3 @@ func (u *User) GetTypedAccount() (Account, error) {
 	}
 	return nil, newError("Unknown account type: ", u.Account.Type)
 }
-
-func (u *User) GetSettings() UserSettings {
-	settings := UserSettings{}
-	switch u.Level {
-	case 0:
-		settings.PayloadTimeout = time.Second * 30
-	case 1:
-		settings.PayloadTimeout = time.Minute * 2
-	default:
-		settings.PayloadTimeout = time.Minute * 5
-	}
-	return settings
-}
-
-type UserSettings struct {
-	PayloadTimeout time.Duration
-}

+ 1 - 0
main/distro/all/all.go

@@ -4,6 +4,7 @@ import (
 	// The following are necessary as they register handlers in their init functions.
 	_ "v2ray.com/core/app/dispatcher/impl"
 	_ "v2ray.com/core/app/dns/server"
+	_ "v2ray.com/core/app/policy/manager"
 	_ "v2ray.com/core/app/proxyman/inbound"
 	_ "v2ray.com/core/app/proxyman/outbound"
 	_ "v2ray.com/core/app/router"

+ 29 - 19
proxy/dokodemo/config.pb.go

@@ -23,6 +23,7 @@ type Config struct {
 	NetworkList    *v2ray_core_common_net1.NetworkList `protobuf:"bytes,3,opt,name=network_list,json=networkList" json:"network_list,omitempty"`
 	Timeout        uint32                              `protobuf:"varint,4,opt,name=timeout" json:"timeout,omitempty"`
 	FollowRedirect bool                                `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect" json:"follow_redirect,omitempty"`
+	UserLevel      uint32                              `protobuf:"varint,6,opt,name=user_level,json=userLevel" json:"user_level,omitempty"`
 }
 
 func (m *Config) Reset()                    { *m = Config{} }
@@ -65,6 +66,13 @@ func (m *Config) GetFollowRedirect() bool {
 	return false
 }
 
+func (m *Config) GetUserLevel() uint32 {
+	if m != nil {
+		return m.UserLevel
+	}
+	return 0
+}
+
 func init() {
 	proto.RegisterType((*Config)(nil), "v2ray.core.proxy.dokodemo.Config")
 }
@@ -72,23 +80,25 @@ func init() {
 func init() { proto.RegisterFile("v2ray.com/core/proxy/dokodemo/config.proto", fileDescriptor0) }
 
 var fileDescriptor0 = []byte{
-	// 286 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xcf, 0x4a, 0x03, 0x31,
-	0x10, 0x87, 0x49, 0xad, 0x6d, 0x49, 0xfd, 0x03, 0x39, 0xa5, 0x82, 0x50, 0x7b, 0x69, 0xf1, 0x90,
-	0x40, 0x3d, 0x7a, 0xb3, 0x15, 0x11, 0x44, 0x97, 0x1c, 0x3c, 0x78, 0x29, 0x6b, 0x36, 0x95, 0xd0,
-	0x4d, 0x66, 0x99, 0x8d, 0xd6, 0x7d, 0x25, 0x5f, 0xcb, 0x17, 0x11, 0xb3, 0xbb, 0x28, 0x42, 0xbd,
-	0xcd, 0x4c, 0xbe, 0x7c, 0x33, 0xfc, 0xe8, 0xf9, 0xdb, 0x1c, 0xd3, 0x4a, 0x68, 0x70, 0x52, 0x03,
-	0x1a, 0x59, 0x20, 0xbc, 0x57, 0x32, 0x83, 0x0d, 0x64, 0xc6, 0x81, 0xd4, 0xe0, 0xd7, 0xf6, 0x45,
-	0x14, 0x08, 0x01, 0xd8, 0xa8, 0x65, 0xd1, 0x88, 0xc8, 0x89, 0x96, 0x3b, 0x99, 0xfe, 0xd1, 0x68,
-	0x70, 0x0e, 0xbc, 0xf4, 0x26, 0xc8, 0x34, 0xcb, 0xd0, 0x94, 0x65, 0xed, 0xf8, 0x0f, 0xf4, 0x26,
-	0x6c, 0x01, 0x37, 0x35, 0x38, 0xf9, 0x24, 0xb4, 0xb7, 0x88, 0xdb, 0xd9, 0x25, 0xed, 0x37, 0x12,
-	0x4e, 0xc6, 0x64, 0x36, 0x9c, 0x9f, 0x89, 0x5f, 0x97, 0xd4, 0x06, 0xe1, 0x4d, 0x10, 0xb7, 0xc9,
-	0x03, 0x2e, 0xc1, 0xa5, 0xd6, 0xab, 0xf6, 0x07, 0x63, 0xb4, 0x5b, 0x00, 0x06, 0xde, 0x19, 0x93,
-	0xd9, 0xa1, 0x8a, 0x35, 0xbb, 0xa6, 0x07, 0xcd, 0xb2, 0x55, 0x6e, 0xcb, 0xc0, 0xf7, 0xa2, 0x75,
-	0xb2, 0xc3, 0x7a, 0x5f, 0xa3, 0x77, 0xb6, 0x0c, 0x6a, 0xe8, 0x7f, 0x1a, 0xc6, 0x69, 0x3f, 0x58,
-	0x67, 0xe0, 0x35, 0xf0, 0x6e, 0xb4, 0xb7, 0x2d, 0x9b, 0xd2, 0xe3, 0x35, 0xe4, 0x39, 0x6c, 0x57,
-	0x68, 0x32, 0x8b, 0x46, 0x07, 0xbe, 0x3f, 0x26, 0xb3, 0x81, 0x3a, 0xaa, 0xc7, 0xaa, 0x99, 0x5e,
-	0xdd, 0xd0, 0x53, 0x0d, 0x4e, 0xec, 0x0c, 0x36, 0x21, 0x4f, 0x83, 0xb6, 0xfe, 0xe8, 0x8c, 0x1e,
-	0xe7, 0x2a, 0xad, 0xc4, 0xe2, 0x9b, 0x4b, 0x22, 0xb7, 0x6c, 0xde, 0x9e, 0x7b, 0x31, 0xb5, 0x8b,
-	0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf9, 0x1c, 0xb0, 0xd9, 0xd0, 0x01, 0x00, 0x00,
+	// 310 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xcf, 0x4e, 0xc2, 0x40,
+	0x10, 0x87, 0xd3, 0x8a, 0x80, 0x8b, 0x7f, 0x92, 0x3d, 0x2d, 0x46, 0x12, 0xe4, 0x02, 0xf1, 0xb0,
+	0x4d, 0xf0, 0xe8, 0x0d, 0x30, 0xc6, 0x84, 0x28, 0xd9, 0x83, 0x07, 0x2f, 0xa4, 0xb6, 0x83, 0x69,
+	0xe8, 0xee, 0x90, 0xe9, 0x02, 0xf6, 0x05, 0x7c, 0x18, 0x9f, 0xd2, 0x74, 0xdb, 0x46, 0x63, 0x82,
+	0xb7, 0xd9, 0xdf, 0x7e, 0xf3, 0xcd, 0x64, 0xd8, 0xcd, 0x6e, 0x4c, 0x61, 0x2e, 0x23, 0xd4, 0x41,
+	0x84, 0x04, 0xc1, 0x86, 0xf0, 0x23, 0x0f, 0x62, 0x5c, 0x63, 0x0c, 0x1a, 0x83, 0x08, 0xcd, 0x2a,
+	0x79, 0x97, 0x1b, 0x42, 0x8b, 0xbc, 0x5b, 0xb3, 0x04, 0xd2, 0x71, 0xb2, 0xe6, 0x2e, 0x87, 0x7f,
+	0x34, 0x11, 0x6a, 0x8d, 0x26, 0x30, 0x60, 0x83, 0x30, 0x8e, 0x09, 0xb2, 0xac, 0x74, 0xfc, 0x07,
+	0x1a, 0xb0, 0x7b, 0xa4, 0x75, 0x09, 0x0e, 0x3e, 0x7d, 0xd6, 0x9c, 0xba, 0xe9, 0xfc, 0x8e, 0xb5,
+	0x2a, 0x89, 0xf0, 0xfa, 0xde, 0xa8, 0x33, 0xbe, 0x96, 0xbf, 0x36, 0x29, 0x0d, 0xd2, 0x80, 0x95,
+	0x8f, 0x8b, 0x67, 0x9a, 0xa1, 0x0e, 0x13, 0xa3, 0xea, 0x0e, 0xce, 0x59, 0x63, 0x83, 0x64, 0x85,
+	0xdf, 0xf7, 0x46, 0x67, 0xca, 0xd5, 0xfc, 0x9e, 0x9d, 0x56, 0xc3, 0x96, 0x69, 0x92, 0x59, 0x71,
+	0xe4, 0xac, 0x83, 0x03, 0xd6, 0xa7, 0x12, 0x9d, 0x27, 0x99, 0x55, 0x1d, 0xf3, 0xf3, 0xe0, 0x57,
+	0xac, 0x65, 0x13, 0x0d, 0xb8, 0xb5, 0xa2, 0x51, 0xd8, 0x27, 0xbe, 0xf0, 0x54, 0x1d, 0xf1, 0x21,
+	0xbb, 0x58, 0x61, 0x9a, 0xe2, 0x7e, 0x49, 0x10, 0x27, 0x04, 0x91, 0x15, 0xc7, 0x7d, 0x6f, 0xd4,
+	0x56, 0xe7, 0x65, 0xac, 0xaa, 0x94, 0xf7, 0x18, 0xdb, 0x66, 0x40, 0xcb, 0x14, 0x76, 0x90, 0x8a,
+	0xa6, 0xdb, 0xf3, 0xa4, 0x48, 0xe6, 0x45, 0x30, 0x79, 0x60, 0xbd, 0x08, 0xb5, 0x3c, 0x78, 0xfb,
+	0x85, 0xf7, 0xda, 0xae, 0xeb, 0x2f, 0xbf, 0xfb, 0x32, 0x56, 0x61, 0x2e, 0xa7, 0x05, 0xb7, 0x70,
+	0xdc, 0xac, 0xfa, 0x7b, 0x6b, 0xba, 0xc3, 0xde, 0x7e, 0x07, 0x00, 0x00, 0xff, 0xff, 0x33, 0x8e,
+	0x70, 0xce, 0xf3, 0x01, 0x00, 0x00,
 }

+ 2 - 1
proxy/dokodemo/config.proto

@@ -13,6 +13,7 @@ message Config {
   v2ray.core.common.net.IPOrDomain address = 1;
   uint32 port = 2;
   v2ray.core.common.net.NetworkList network_list = 3;
-  uint32 timeout = 4;
+  uint32 timeout = 4 [deprecated = true];
   bool follow_redirect = 5;
+  uint32 user_level = 6;
 }

+ 17 - 8
proxy/dokodemo/dokodemo.go

@@ -4,11 +4,11 @@ package dokodemo
 
 import (
 	"context"
-	"time"
 
 	"v2ray.com/core/app"
 	"v2ray.com/core/app/dispatcher"
 	"v2ray.com/core/app/log"
+	"v2ray.com/core/app/policy"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
 	"v2ray.com/core/common/net"
@@ -22,6 +22,7 @@ type DokodemoDoor struct {
 	config  *Config
 	address net.Address
 	port    net.Port
+	policy  policy.Policy
 }
 
 func New(ctx context.Context, config *Config) (*DokodemoDoor, error) {
@@ -37,6 +38,17 @@ func New(ctx context.Context, config *Config) (*DokodemoDoor, error) {
 		address: config.GetPredefinedAddress(),
 		port:    net.Port(config.Port),
 	}
+	space.OnInitialize(func() error {
+		pm := policy.PolicyFromSpace(space)
+		if pm == nil {
+			return newError("Policy not found in space.")
+		}
+		d.policy = pm.GetPolicy(config.UserLevel)
+		if config.Timeout > 0 && config.UserLevel == 0 {
+			d.policy.Timeout.ConnectionIdle.Value = config.Timeout
+		}
+		return nil
+	})
 	return d, nil
 }
 
@@ -60,13 +72,8 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
 		return newError("unable to get destination")
 	}
 
-	timeout := time.Second * time.Duration(d.config.Timeout)
-	if timeout == 0 {
-		timeout = time.Minute * 5
-	}
-
 	ctx, cancel := context.WithCancel(ctx)
-	timer := signal.CancelAfterInactivity(ctx, cancel, timeout)
+	timer := signal.CancelAfterInactivity(ctx, cancel, d.policy.Timeout.ConnectionIdle.Duration())
 
 	inboundRay, err := dispatcher.Dispatch(ctx, dest)
 	if err != nil {
@@ -82,6 +89,8 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
 			return newError("failed to transport request").Base(err)
 		}
 
+		timer.SetTimeout(d.policy.Timeout.DownlinkOnly.Duration())
+
 		return nil
 	})
 
@@ -107,7 +116,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
 			return newError("failed to transport response").Base(err)
 		}
 
-		timer.SetTimeout(time.Second * 2)
+		timer.SetTimeout(d.policy.Timeout.UplinkOnly.Duration())
 
 		return nil
 	})

+ 31 - 21
proxy/freedom/config.pb.go

@@ -57,6 +57,7 @@ type Config struct {
 	DomainStrategy      Config_DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,enum=v2ray.core.proxy.freedom.Config_DomainStrategy" json:"domain_strategy,omitempty"`
 	Timeout             uint32                `protobuf:"varint,2,opt,name=timeout" json:"timeout,omitempty"`
 	DestinationOverride *DestinationOverride  `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride" json:"destination_override,omitempty"`
+	UserLevel           uint32                `protobuf:"varint,4,opt,name=user_level,json=userLevel" json:"user_level,omitempty"`
 }
 
 func (m *Config) Reset()                    { *m = Config{} }
@@ -85,6 +86,13 @@ func (m *Config) GetDestinationOverride() *DestinationOverride {
 	return nil
 }
 
+func (m *Config) GetUserLevel() uint32 {
+	if m != nil {
+		return m.UserLevel
+	}
+	return 0
+}
+
 func init() {
 	proto.RegisterType((*DestinationOverride)(nil), "v2ray.core.proxy.freedom.DestinationOverride")
 	proto.RegisterType((*Config)(nil), "v2ray.core.proxy.freedom.Config")
@@ -94,25 +102,27 @@ func init() {
 func init() { proto.RegisterFile("v2ray.com/core/proxy/freedom/config.proto", fileDescriptor0) }
 
 var fileDescriptor0 = []byte{
-	// 318 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0xcf, 0x4a, 0xc3, 0x40,
-	0x10, 0xc6, 0x4d, 0xc5, 0x14, 0x47, 0xac, 0x65, 0xeb, 0x21, 0x88, 0x87, 0xd2, 0x8b, 0x55, 0x70,
-	0x23, 0xf1, 0x09, 0xec, 0x1f, 0xa1, 0x27, 0x4b, 0x82, 0xa2, 0x5e, 0x62, 0xcc, 0x4e, 0xcb, 0x82,
-	0xd9, 0x09, 0x9b, 0xb5, 0x98, 0x27, 0xf0, 0x5d, 0x7c, 0x4a, 0x71, 0x93, 0xa2, 0x55, 0x73, 0xdb,
-	0x99, 0xfd, 0x7d, 0xdf, 0xcc, 0x37, 0x70, 0xba, 0x0a, 0x74, 0x52, 0xf2, 0x94, 0x32, 0x3f, 0x25,
-	0x8d, 0x7e, 0xae, 0xe9, 0xad, 0xf4, 0x17, 0x1a, 0x51, 0xd8, 0x96, 0x5a, 0xc8, 0x25, 0xcf, 0x35,
-	0x19, 0x62, 0xde, 0x1a, 0xd5, 0xc8, 0x2d, 0xc6, 0x6b, 0xec, 0xe8, 0xe2, 0x97, 0x49, 0x4a, 0x59,
-	0x46, 0xca, 0xb7, 0xb2, 0x94, 0x5e, 0xfc, 0x02, 0xf5, 0x0a, 0x75, 0x5c, 0xe4, 0x98, 0x56, 0x5e,
-	0x83, 0x07, 0xe8, 0x4d, 0xb0, 0x30, 0x52, 0x25, 0x46, 0x92, 0xba, 0x59, 0xa1, 0xd6, 0x52, 0x20,
-	0x1b, 0x81, 0x5b, 0xb1, 0x9e, 0xd3, 0x77, 0x86, 0x7b, 0xc1, 0x19, 0xff, 0x31, 0xb3, 0x72, 0xe5,
-	0x6b, 0x57, 0x1e, 0x59, 0x72, 0xaa, 0x44, 0x4e, 0x52, 0x99, 0xb0, 0x56, 0x0e, 0xde, 0x5b, 0xe0,
-	0x8e, 0xed, 0xde, 0xec, 0x1e, 0x0e, 0x04, 0x65, 0x89, 0x54, 0x71, 0x61, 0x74, 0x62, 0x70, 0x59,
-	0x5a, 0xdf, 0x4e, 0xe0, 0xf3, 0xa6, 0x2c, 0xbc, 0x92, 0xf2, 0x89, 0xd5, 0x45, 0xb5, 0x2c, 0xec,
-	0x88, 0x8d, 0x9a, 0x79, 0xd0, 0x36, 0x32, 0x43, 0x7a, 0x35, 0x5e, 0xab, 0xef, 0x0c, 0xf7, 0xc3,
-	0x75, 0xc9, 0x9e, 0xe0, 0x50, 0x7c, 0x27, 0x8b, 0xa9, 0x8e, 0xe6, 0x6d, 0xdb, 0x40, 0xe7, 0xcd,
-	0x83, 0xff, 0xb9, 0x47, 0xd8, 0x13, 0x7f, 0x9b, 0x83, 0x13, 0xe8, 0x6c, 0x6e, 0xc7, 0x76, 0x61,
-	0xe7, 0x2a, 0x8a, 0x67, 0x51, 0x77, 0x8b, 0x01, 0xb8, 0xb7, 0xd1, 0x34, 0x9e, 0xcd, 0xbb, 0xce,
-	0x68, 0x02, 0xc7, 0x29, 0x65, 0x8d, 0x13, 0xe7, 0xce, 0x63, 0xbb, 0x7e, 0x7e, 0xb4, 0xbc, 0xbb,
-	0x20, 0x4c, 0x4a, 0x3e, 0xfe, 0xa2, 0xe6, 0x96, 0xba, 0xae, 0xbe, 0x9e, 0x5d, 0x7b, 0xf0, 0xcb,
-	0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x45, 0xb3, 0x84, 0x2e, 0x2a, 0x02, 0x00, 0x00,
+	// 340 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0x6f, 0x4b, 0x83, 0x50,
+	0x14, 0xc6, 0xd3, 0xca, 0xb1, 0x13, 0xad, 0xe1, 0x7a, 0x21, 0xb1, 0x60, 0xec, 0x4d, 0x2b, 0xe8,
+	0x1a, 0xf6, 0x09, 0xda, 0x9f, 0x60, 0x10, 0x34, 0x94, 0xa2, 0x7a, 0x63, 0xa6, 0x67, 0x43, 0x98,
+	0xf7, 0xc8, 0xf5, 0x4e, 0xf2, 0x2b, 0xed, 0x53, 0x86, 0x57, 0x47, 0x2d, 0xb6, 0x77, 0xfa, 0xf8,
+	0x7b, 0x9e, 0x73, 0x9e, 0x23, 0x5c, 0xe7, 0x8e, 0x08, 0x0a, 0x16, 0x52, 0x62, 0x87, 0x24, 0xd0,
+	0x4e, 0x05, 0x7d, 0x17, 0xf6, 0x5c, 0x20, 0x46, 0x4a, 0xe2, 0xf3, 0x78, 0xc1, 0x52, 0x41, 0x92,
+	0x4c, 0x6b, 0x83, 0x0a, 0x64, 0x0a, 0x63, 0x35, 0x76, 0x71, 0xf7, 0x2f, 0x24, 0xa4, 0x24, 0x21,
+	0x6e, 0x2b, 0x5b, 0x48, 0x4b, 0x3b, 0x43, 0x91, 0xa3, 0xf0, 0xb3, 0x14, 0xc3, 0x2a, 0xab, 0xff,
+	0x0e, 0x9d, 0x31, 0x66, 0x32, 0xe6, 0x81, 0x8c, 0x89, 0x3f, 0xe7, 0x28, 0x44, 0x1c, 0xa1, 0x39,
+	0x04, 0xa3, 0x62, 0x2d, 0xad, 0xa7, 0x0d, 0x4e, 0x9c, 0x1b, 0xf6, 0x67, 0x66, 0x95, 0xca, 0x36,
+	0xa9, 0xcc, 0x53, 0xe4, 0x84, 0x47, 0x29, 0xc5, 0x5c, 0xba, 0xb5, 0xb3, 0xbf, 0xd6, 0xc1, 0x18,
+	0xa9, 0xbd, 0xcd, 0x37, 0x38, 0x8b, 0x28, 0x09, 0x62, 0xee, 0x67, 0x52, 0x04, 0x12, 0x17, 0x85,
+	0xca, 0x6d, 0x39, 0x36, 0xdb, 0xd7, 0x85, 0x55, 0x56, 0x36, 0x56, 0x3e, 0xaf, 0xb6, 0xb9, 0xad,
+	0x68, 0xeb, 0xdd, 0xec, 0x42, 0x43, 0xc6, 0x09, 0xd2, 0x4a, 0x5a, 0x7a, 0x4f, 0x1b, 0x9c, 0x0e,
+	0x75, 0x4b, 0x73, 0x37, 0x92, 0xf9, 0x09, 0xe7, 0xd1, 0x6f, 0x3b, 0x9f, 0xea, 0x7a, 0xd6, 0xa1,
+	0x2a, 0x75, 0xbb, 0x7f, 0xf8, 0x8e, 0x9b, 0xb8, 0x9d, 0x68, 0xc7, 0xa1, 0x2e, 0x01, 0x56, 0x19,
+	0x0a, 0x7f, 0x89, 0x39, 0x2e, 0xad, 0xa3, 0x72, 0x05, 0xb7, 0x59, 0x2a, 0x4f, 0xa5, 0xd0, 0xbf,
+	0x82, 0xd6, 0x76, 0x01, 0xb3, 0x09, 0xc7, 0x0f, 0x9e, 0x3f, 0xf5, 0xda, 0x07, 0x26, 0x80, 0xf1,
+	0xe2, 0x4d, 0xfc, 0xe9, 0xac, 0xad, 0x0d, 0xc7, 0xd0, 0x0d, 0x29, 0xd9, 0xbb, 0xd0, 0x4c, 0xfb,
+	0x68, 0xd4, 0x8f, 0x6b, 0xdd, 0x7a, 0x75, 0xdc, 0xa0, 0x60, 0xa3, 0x92, 0x9a, 0x29, 0xea, 0xb1,
+	0xfa, 0xf4, 0x65, 0xa8, 0x7f, 0x72, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xb7, 0x5e, 0xda, 0x4d,
+	0x4d, 0x02, 0x00, 0x00,
 }

+ 2 - 1
proxy/freedom/config.proto

@@ -18,6 +18,7 @@ message Config {
     USE_IP = 1;
   }
   DomainStrategy domain_strategy = 1;
-  uint32 timeout = 2;
+  uint32 timeout = 2 [deprecated = true];
   DestinationOverride destination_override = 3;
+  uint32 user_level = 4;
 }

+ 13 - 6
proxy/freedom/freedom.go

@@ -4,11 +4,11 @@ package freedom
 
 import (
 	"context"
-	"time"
 
 	"v2ray.com/core/app"
 	"v2ray.com/core/app/dns"
 	"v2ray.com/core/app/log"
+	"v2ray.com/core/app/policy"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
 	"v2ray.com/core/common/dice"
@@ -26,6 +26,7 @@ type Handler struct {
 	timeout        uint32
 	dns            dns.Server
 	destOverride   *DestinationOverride
+	policy         policy.Policy
 }
 
 // New creates a new Freedom handler.
@@ -46,6 +47,14 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
 				return newError("DNS server is not found in the space")
 			}
 		}
+		pm := policy.PolicyFromSpace(space)
+		if pm == nil {
+			return newError("Policy not found in space.")
+		}
+		f.policy = pm.GetPolicy(config.UserLevel)
+		if config.Timeout > 0 && config.UserLevel == 0 {
+			f.policy.Timeout.ConnectionIdle.Value = config.Timeout
+		}
 		return nil
 	})
 	return f, nil
@@ -109,12 +118,8 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
 	}
 	defer conn.Close()
 
-	timeout := time.Second * time.Duration(h.timeout)
-	if timeout == 0 {
-		timeout = time.Minute * 5
-	}
 	ctx, cancel := context.WithCancel(ctx)
-	timer := signal.CancelAfterInactivity(ctx, cancel, timeout)
+	timer := signal.CancelAfterInactivity(ctx, cancel, h.policy.Timeout.ConnectionIdle.Duration())
 
 	requestDone := signal.ExecuteAsync(func() error {
 		var writer buf.Writer
@@ -126,6 +131,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
 		if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil {
 			return newError("failed to process request").Base(err)
 		}
+		timer.SetTimeout(h.policy.Timeout.DownlinkOnly.Duration())
 		return nil
 	})
 
@@ -136,6 +142,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
 		if err := buf.Copy(v2reader, output, buf.UpdateActivity(timer)); err != nil {
 			return newError("failed to process response").Base(err)
 		}
+		timer.SetTimeout(h.policy.Timeout.UplinkOnly.Duration())
 		return nil
 	})
 

+ 28 - 18
proxy/http/config.pb.go

@@ -20,6 +20,7 @@ type ServerConfig struct {
 	Timeout          uint32            `protobuf:"varint,1,opt,name=timeout" json:"timeout,omitempty"`
 	Accounts         map[string]string `protobuf:"bytes,2,rep,name=accounts" json:"accounts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
 	AllowTransparent bool              `protobuf:"varint,3,opt,name=allow_transparent,json=allowTransparent" json:"allow_transparent,omitempty"`
+	UserLevel        uint32            `protobuf:"varint,4,opt,name=user_level,json=userLevel" json:"user_level,omitempty"`
 }
 
 func (m *ServerConfig) Reset()                    { *m = ServerConfig{} }
@@ -48,6 +49,13 @@ func (m *ServerConfig) GetAllowTransparent() bool {
 	return false
 }
 
+func (m *ServerConfig) GetUserLevel() uint32 {
+	if m != nil {
+		return m.UserLevel
+	}
+	return 0
+}
+
 // ClientConfig for HTTP proxy client.
 type ClientConfig struct {
 }
@@ -65,22 +73,24 @@ func init() {
 func init() { proto.RegisterFile("v2ray.com/core/proxy/http/config.proto", fileDescriptor0) }
 
 var fileDescriptor0 = []byte{
-	// 271 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x5d, 0x4b, 0xc3, 0x30,
-	0x14, 0x86, 0x49, 0xe7, 0xc7, 0x8c, 0x9b, 0xcc, 0xe0, 0xa0, 0x7a, 0x55, 0x76, 0x21, 0x05, 0x21,
-	0xc5, 0x7a, 0x23, 0xee, 0x4a, 0x8b, 0xe0, 0x8d, 0x30, 0xaa, 0x78, 0xe1, 0x8d, 0xc4, 0x10, 0xb5,
-	0xd8, 0xe6, 0x94, 0xb3, 0xd3, 0x6a, 0xfe, 0x92, 0xff, 0xca, 0x7f, 0x22, 0xcd, 0x9c, 0x1f, 0xb0,
-	0xab, 0xe4, 0x9c, 0xf7, 0xc9, 0xc3, 0x4b, 0xf8, 0x61, 0x9b, 0xa2, 0x72, 0x52, 0x43, 0x95, 0x68,
-	0x40, 0x93, 0xd4, 0x08, 0xef, 0x2e, 0x79, 0x21, 0xaa, 0x13, 0x0d, 0xf6, 0xa9, 0x78, 0x96, 0x35,
-	0x02, 0x81, 0x18, 0x2f, 0x39, 0x34, 0xd2, 0x33, 0xb2, 0x63, 0x26, 0x9f, 0x8c, 0x0f, 0x6e, 0x0c,
-	0xb6, 0x06, 0x33, 0x4f, 0x8b, 0x90, 0x6f, 0x52, 0x51, 0x19, 0x68, 0x28, 0x64, 0x11, 0x8b, 0x87,
-	0xf9, 0x72, 0x14, 0xd7, 0xbc, 0xaf, 0xb4, 0x86, 0xc6, 0xd2, 0x3c, 0x0c, 0xa2, 0x5e, 0xbc, 0x9d,
-	0x1e, 0xcb, 0x95, 0x52, 0xf9, 0x57, 0x28, 0xcf, 0xbf, 0xdf, 0x5c, 0x5a, 0x42, 0x97, 0xff, 0x28,
-	0xc4, 0x11, 0xdf, 0x55, 0x65, 0x09, 0x6f, 0x0f, 0x84, 0xca, 0xce, 0x6b, 0x85, 0xc6, 0x52, 0xd8,
-	0x8b, 0x58, 0xdc, 0xcf, 0x47, 0x3e, 0xb8, 0xfd, 0xdd, 0x1f, 0x4c, 0xf9, 0xf0, 0x9f, 0x47, 0x8c,
-	0x78, 0xef, 0xd5, 0x38, 0x5f, 0x71, 0x2b, 0xef, 0xae, 0x62, 0x8f, 0xaf, 0xb7, 0xaa, 0x6c, 0x4c,
-	0x18, 0xf8, 0xdd, 0x62, 0x38, 0x0b, 0x4e, 0xd9, 0x64, 0x87, 0x0f, 0xb2, 0xb2, 0x30, 0x96, 0x16,
-	0x8d, 0x2e, 0xa6, 0x7c, 0x5f, 0x43, 0xb5, 0xba, 0xfb, 0x8c, 0xdd, 0xaf, 0x75, 0xe7, 0x47, 0x30,
-	0xbe, 0x4b, 0x73, 0xe5, 0x64, 0xd6, 0xe5, 0x33, 0x9f, 0x5f, 0x11, 0xd5, 0x8f, 0x1b, 0xfe, 0x3b,
-	0x4f, 0xbe, 0x02, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x08, 0xbc, 0x0a, 0x78, 0x01, 0x00, 0x00,
+	// 296 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xcf, 0x4a, 0x33, 0x31,
+	0x14, 0xc5, 0x99, 0x69, 0xbf, 0xcf, 0xf6, 0xda, 0x4a, 0x0d, 0x16, 0x46, 0x51, 0x28, 0x5d, 0x48,
+	0x41, 0xc8, 0x60, 0xdd, 0x88, 0x5d, 0xd9, 0x22, 0xb8, 0x50, 0x28, 0x51, 0x5c, 0xb8, 0x29, 0x31,
+	0x5c, 0xb5, 0x98, 0x26, 0x43, 0xe6, 0xce, 0xe8, 0xec, 0x7d, 0x1a, 0x9f, 0x52, 0x92, 0x5a, 0xff,
+	0x40, 0x57, 0x49, 0x7e, 0xe7, 0xe4, 0xe4, 0x9e, 0xc0, 0x61, 0x39, 0x74, 0xb2, 0xe2, 0xca, 0x2e,
+	0x52, 0x65, 0x1d, 0xa6, 0x99, 0xb3, 0x6f, 0x55, 0xfa, 0x4c, 0x94, 0xa5, 0xca, 0x9a, 0xc7, 0xf9,
+	0x13, 0xcf, 0x9c, 0x25, 0xcb, 0xba, 0x2b, 0x9f, 0x43, 0x1e, 0x3c, 0xdc, 0x7b, 0xfa, 0xef, 0x31,
+	0xb4, 0x6e, 0xd0, 0x95, 0xe8, 0x26, 0xc1, 0xcd, 0xf6, 0x61, 0x83, 0xe6, 0x0b, 0xb4, 0x05, 0x25,
+	0x51, 0x2f, 0x1a, 0xb4, 0xc7, 0x71, 0x12, 0x89, 0x15, 0x62, 0xd7, 0xd0, 0x90, 0x4a, 0xd9, 0xc2,
+	0x50, 0x9e, 0xc4, 0xbd, 0xda, 0x60, 0x73, 0x78, 0xcc, 0xd7, 0x06, 0xf3, 0xdf, 0xa1, 0xfc, 0xfc,
+	0xeb, 0xce, 0x85, 0x21, 0x57, 0x89, 0xef, 0x08, 0x76, 0x04, 0xdb, 0x52, 0x6b, 0xfb, 0x3a, 0x23,
+	0x27, 0x4d, 0x9e, 0x49, 0x87, 0x86, 0x92, 0x5a, 0x2f, 0x1a, 0x34, 0x44, 0x27, 0x08, 0xb7, 0x3f,
+	0x9c, 0x1d, 0x00, 0x14, 0x39, 0xba, 0x99, 0xc6, 0x12, 0x75, 0x52, 0xf7, 0xc3, 0x89, 0xa6, 0x27,
+	0x57, 0x1e, 0xec, 0x8d, 0xa0, 0xfd, 0xe7, 0x19, 0xd6, 0x81, 0xda, 0x0b, 0x56, 0xa1, 0x45, 0x53,
+	0xf8, 0x2d, 0xdb, 0x81, 0x7f, 0xa5, 0xd4, 0x05, 0x26, 0x71, 0x60, 0xcb, 0xc3, 0x59, 0x7c, 0x1a,
+	0xf5, 0xb7, 0xa0, 0x35, 0xd1, 0x73, 0x34, 0xb4, 0x1c, 0x78, 0x3c, 0x82, 0x5d, 0x65, 0x17, 0xeb,
+	0xab, 0x4d, 0xa3, 0xfb, 0xba, 0x5f, 0x3f, 0xe2, 0xee, 0xdd, 0x50, 0xc8, 0x8a, 0x4f, 0xbc, 0x3e,
+	0x0d, 0xfa, 0x25, 0x51, 0xf6, 0xf0, 0x3f, 0xfc, 0xf8, 0xc9, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff,
+	0x69, 0x94, 0x9f, 0xa7, 0x9b, 0x01, 0x00, 0x00,
 }

+ 2 - 1
proxy/http/config.proto

@@ -8,9 +8,10 @@ option java_multiple_files = true;
 
 // Config for HTTP proxy server.
 message ServerConfig {
-  uint32 timeout = 1;
+  uint32 timeout = 1 [deprecated = true];
   map<string, string> accounts = 2;
   bool allow_transparent = 3;
+  uint32 user_level = 4;
 }
 
 // ClientConfig for HTTP proxy client.

+ 17 - 7
proxy/http/server.go

@@ -13,6 +13,7 @@ import (
 	"v2ray.com/core/app"
 	"v2ray.com/core/app/dispatcher"
 	"v2ray.com/core/app/log"
+	"v2ray.com/core/app/policy"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
 	"v2ray.com/core/common/errors"
@@ -24,6 +25,7 @@ import (
 // Server is a HTTP proxy server.
 type Server struct {
 	config *ServerConfig
+	policy policy.Policy
 }
 
 // NewServer creates a new HTTP inbound handler.
@@ -35,6 +37,17 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
 	s := &Server{
 		config: config,
 	}
+	space.OnInitialize(func() error {
+		pm := policy.PolicyFromSpace(space)
+		if pm == nil {
+			return newError("Policy not found in space.")
+		}
+		s.policy = pm.GetPolicy(config.UserLevel)
+		if config.Timeout > 0 && config.UserLevel == 0 {
+			s.policy.Timeout.ConnectionIdle.Value = config.Timeout
+		}
+		return nil
+	})
 	return s, nil
 }
 
@@ -94,7 +107,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet
 	reader := bufio.NewReaderSize(readerOnly{conn}, buf.Size)
 
 Start:
-	conn.SetReadDeadline(time.Now().Add(time.Second * 16))
+	conn.SetReadDeadline(time.Now().Add(s.policy.Timeout.Handshake.Duration()))
 
 	request, err := http.ReadRequest(reader)
 	if err != nil {
@@ -157,12 +170,8 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
 		return newError("failed to write back OK response").Base(err)
 	}
 
-	timeout := time.Second * time.Duration(s.config.Timeout)
-	if timeout == 0 {
-		timeout = time.Minute * 5
-	}
 	ctx, cancel := context.WithCancel(ctx)
-	timer := signal.CancelAfterInactivity(ctx, cancel, timeout)
+	timer := signal.CancelAfterInactivity(ctx, cancel, s.policy.Timeout.ConnectionIdle.Duration())
 	ray, err := dispatcher.Dispatch(ctx, dest)
 	if err != nil {
 		return err
@@ -181,6 +190,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
 
 	requestDone := signal.ExecuteAsync(func() error {
 		defer ray.InboundInput().Close()
+		defer timer.SetTimeout(s.policy.Timeout.DownlinkOnly.Duration())
 
 		v2reader := buf.NewReader(conn)
 		return buf.Copy(v2reader, ray.InboundInput(), buf.UpdateActivity(timer))
@@ -191,7 +201,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
 		if err := buf.Copy(ray.InboundOutput(), v2writer, buf.UpdateActivity(timer)); err != nil {
 			return err
 		}
-		timer.SetTimeout(time.Second * 2)
+		timer.SetTimeout(s.policy.Timeout.UplinkOnly.Duration())
 		return nil
 	})
 

+ 20 - 3
proxy/shadowsocks/client.go

@@ -2,9 +2,10 @@ package shadowsocks
 
 import (
 	"context"
-	"time"
 
+	"v2ray.com/core/app"
 	"v2ray.com/core/app/log"
+	"v2ray.com/core/app/policy"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
 	"v2ray.com/core/common/net"
@@ -18,7 +19,8 @@ import (
 
 // Client is a inbound handler for Shadowsocks protocol
 type Client struct {
-	serverPicker protocol.ServerPicker
+	serverPicker  protocol.ServerPicker
+	policyManager policy.Interface
 }
 
 // NewClient create a new Shadowsocks client.
@@ -33,6 +35,18 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
 	client := &Client{
 		serverPicker: protocol.NewRoundRobinServerPicker(serverList),
 	}
+	space := app.SpaceFromContext(ctx)
+	if space == nil {
+		return nil, newError("Space not found.")
+	}
+	space.OnInitialize(func() error {
+		pm := policy.PolicyFromSpace(space)
+		if pm == nil {
+			return newError("Policy not found in space.")
+		}
+		client.policyManager = pm
+		return nil
+	})
 
 	return client, nil
 }
@@ -90,8 +104,9 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
 		request.Option |= RequestOptionOneTimeAuth
 	}
 
+	sessionPolicy := v.policyManager.GetPolicy(user.Level)
 	ctx, cancel := context.WithCancel(ctx)
-	timer := signal.CancelAfterInactivity(ctx, cancel, time.Minute*5)
+	timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration())
 
 	if request.Command == protocol.RequestCommandTCP {
 		bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
@@ -105,11 +120,13 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
 		}
 
 		requestDone := signal.ExecuteAsync(func() error {
+			defer timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration())
 			return buf.Copy(outboundRay.OutboundInput(), bodyWriter, buf.UpdateActivity(timer))
 		})
 
 		responseDone := signal.ExecuteAsync(func() error {
 			defer outboundRay.OutboundOutput().Close()
+			defer timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration())
 
 			responseReader, err := ReadTCPResponse(user, conn)
 			if err != nil {

+ 1 - 1
proxy/shadowsocks/config.proto

@@ -38,4 +38,4 @@ message ServerConfig {
 
 message ClientConfig {
   repeated v2ray.core.common.protocol.ServerEndpoint server = 1;
-}
+}

+ 19 - 7
proxy/shadowsocks/server.go

@@ -7,6 +7,7 @@ import (
 	"v2ray.com/core/app"
 	"v2ray.com/core/app/dispatcher"
 	"v2ray.com/core/app/log"
+	"v2ray.com/core/app/policy"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
 	"v2ray.com/core/common/net"
@@ -18,9 +19,10 @@ import (
 )
 
 type Server struct {
-	config  *ServerConfig
-	user    *protocol.User
-	account *ShadowsocksAccount
+	config        *ServerConfig
+	user          *protocol.User
+	account       *ShadowsocksAccount
+	policyManager policy.Interface
 }
 
 // NewServer create a new Shadowsocks server.
@@ -45,6 +47,15 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
 		account: account,
 	}
 
+	space.OnInitialize(func() error {
+		pm := policy.PolicyFromSpace(space)
+		if pm == nil {
+			return newError("Policy not found in space.")
+		}
+		s.policyManager = pm
+		return nil
+	})
+
 	return s, nil
 }
 
@@ -128,7 +139,8 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
 }
 
 func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
-	conn.SetReadDeadline(time.Now().Add(time.Second * 8))
+	sessionPolicy := s.policyManager.GetPolicy(s.user.Level)
+	conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeout.Handshake.Duration()))
 	bufferedReader := buf.NewBufferedReader(buf.NewReader(conn))
 	request, bodyReader, err := ReadTCPSession(s.user, bufferedReader)
 	if err != nil {
@@ -145,9 +157,8 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
 
 	ctx = protocol.ContextWithUser(ctx, request.User)
 
-	userSettings := s.user.GetSettings()
 	ctx, cancel := context.WithCancel(ctx)
-	timer := signal.CancelAfterInactivity(ctx, cancel, userSettings.PayloadTimeout)
+	timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration())
 	ray, err := dispatcher.Dispatch(ctx, dest)
 	if err != nil {
 		return err
@@ -177,7 +188,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
 			return newError("failed to transport all TCP response").Base(err)
 		}
 
-		timer.SetTimeout(time.Second * 2)
+		timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration())
 
 		return nil
 	})
@@ -188,6 +199,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
 		if err := buf.Copy(bodyReader, ray.InboundInput(), buf.UpdateActivity(timer)); err != nil {
 			return newError("failed to transport all TCP request").Base(err)
 		}
+		timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration())
 		return nil
 	})
 

+ 39 - 30
proxy/socks/config.pb.go

@@ -68,6 +68,7 @@ type ServerConfig struct {
 	Address    *v2ray_core_common_net.IPOrDomain `protobuf:"bytes,3,opt,name=address" json:"address,omitempty"`
 	UdpEnabled bool                              `protobuf:"varint,4,opt,name=udp_enabled,json=udpEnabled" json:"udp_enabled,omitempty"`
 	Timeout    uint32                            `protobuf:"varint,5,opt,name=timeout" json:"timeout,omitempty"`
+	UserLevel  uint32                            `protobuf:"varint,6,opt,name=user_level,json=userLevel" json:"user_level,omitempty"`
 }
 
 func (m *ServerConfig) Reset()                    { *m = ServerConfig{} }
@@ -110,6 +111,13 @@ func (m *ServerConfig) GetTimeout() uint32 {
 	return 0
 }
 
+func (m *ServerConfig) GetUserLevel() uint32 {
+	if m != nil {
+		return m.UserLevel
+	}
+	return 0
+}
+
 type ClientConfig struct {
 	Server []*v2ray_core_common_protocol1.ServerEndpoint `protobuf:"bytes,1,rep,name=server" json:"server,omitempty"`
 }
@@ -136,34 +144,35 @@ func init() {
 func init() { proto.RegisterFile("v2ray.com/core/proxy/socks/config.proto", fileDescriptor0) }
 
 var fileDescriptor0 = []byte{
-	// 449 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xd1, 0x6e, 0xd3, 0x3e,
-	0x14, 0xc6, 0xff, 0x49, 0xff, 0x5d, 0xb3, 0xd3, 0x0e, 0x55, 0x16, 0x9a, 0xa2, 0xdc, 0x10, 0x2a,
-	0x21, 0xa2, 0x5d, 0x38, 0x28, 0xdc, 0x20, 0x26, 0x90, 0xb2, 0xae, 0x12, 0xdc, 0xac, 0x95, 0x3b,
-	0x40, 0xe2, 0xa6, 0xf2, 0x1c, 0xc3, 0xa2, 0x35, 0x76, 0x64, 0x3b, 0x85, 0xbc, 0x12, 0x8f, 0xc3,
-	0x13, 0xa1, 0xda, 0xc9, 0x34, 0x50, 0x77, 0x77, 0x4e, 0xce, 0x77, 0xbe, 0x9c, 0xef, 0x67, 0x78,
-	0xb9, 0xcb, 0x14, 0x6d, 0x31, 0x93, 0x55, 0xca, 0xa4, 0xe2, 0x69, 0xad, 0xe4, 0xcf, 0x36, 0xd5,
-	0x92, 0xdd, 0xe9, 0x94, 0x49, 0xf1, 0xad, 0xfc, 0x8e, 0x6b, 0x25, 0x8d, 0x44, 0xa7, 0xbd, 0x50,
-	0x71, 0x6c, 0x45, 0xd8, 0x8a, 0xa2, 0x7f, 0x0d, 0x98, 0xac, 0x2a, 0x29, 0x52, 0xc1, 0x4d, 0x4a,
-	0x8b, 0x42, 0x71, 0xad, 0x9d, 0x41, 0xf4, 0xea, 0xb0, 0xd0, 0x0e, 0x99, 0xdc, 0xa6, 0x9a, 0xab,
-	0x1d, 0x57, 0x1b, 0x5d, 0x73, 0xe6, 0x36, 0x66, 0x39, 0x8c, 0x72, 0xc6, 0x64, 0x23, 0x0c, 0x8a,
-	0x20, 0x68, 0x34, 0x57, 0x82, 0x56, 0x3c, 0xf4, 0x62, 0x2f, 0x39, 0x26, 0xf7, 0xfd, 0x7e, 0x56,
-	0x53, 0xad, 0x7f, 0x48, 0x55, 0x84, 0xbe, 0x9b, 0xf5, 0xfd, 0xec, 0xb7, 0x0f, 0x93, 0xb5, 0x35,
-	0x9e, 0xdb, 0x30, 0xe8, 0x1d, 0x1c, 0xd3, 0xc6, 0xdc, 0x6e, 0x4c, 0x5b, 0x3b, 0xa7, 0x27, 0x59,
-	0x8c, 0x0f, 0x47, 0xc3, 0x79, 0x63, 0x6e, 0xaf, 0xdb, 0x9a, 0x93, 0x80, 0x76, 0x15, 0xba, 0x82,
-	0x80, 0xba, 0x93, 0x74, 0xe8, 0xc7, 0x83, 0x64, 0x9c, 0x65, 0x8f, 0x6d, 0x3f, 0xfc, 0x2d, 0xee,
-	0x72, 0xe8, 0x85, 0x30, 0xaa, 0x25, 0xf7, 0x1e, 0xe8, 0x1c, 0x46, 0x1d, 0xa5, 0x70, 0x10, 0x7b,
-	0xc9, 0x38, 0x7b, 0xfe, 0xd0, 0xce, 0x21, 0xc2, 0x82, 0x1b, 0xfc, 0x71, 0xb5, 0x54, 0x97, 0xb2,
-	0xa2, 0xa5, 0x20, 0xfd, 0x06, 0x7a, 0x06, 0xe3, 0xa6, 0xa8, 0x37, 0x5c, 0xd0, 0x9b, 0x2d, 0x2f,
-	0xc2, 0xff, 0x63, 0x2f, 0x09, 0x08, 0x34, 0x45, 0xbd, 0x70, 0x5f, 0x50, 0x08, 0x23, 0x53, 0x56,
-	0x5c, 0x36, 0x26, 0x1c, 0xc6, 0x5e, 0x72, 0x42, 0xfa, 0x36, 0x3a, 0x87, 0x93, 0xbf, 0x4e, 0x42,
-	0x53, 0x18, 0xdc, 0xf1, 0xb6, 0x63, 0xbb, 0x2f, 0xd1, 0x53, 0x18, 0xee, 0xe8, 0xb6, 0xe1, 0x1d,
-	0x53, 0xd7, 0xbc, 0xf5, 0xdf, 0x78, 0x33, 0x02, 0x93, 0xf9, 0xb6, 0xe4, 0xc2, 0x74, 0x4c, 0x2f,
-	0xe0, 0xc8, 0x3d, 0x5e, 0xe8, 0x59, 0x24, 0x67, 0x07, 0x32, 0xf4, 0xcf, 0xdc, 0x61, 0x59, 0x88,
-	0xa2, 0x96, 0xa5, 0x30, 0xa4, 0xdb, 0x3c, 0x7b, 0x01, 0x41, 0x8f, 0x1b, 0x8d, 0x61, 0x74, 0xb5,
-	0xdc, 0xe4, 0x9f, 0xae, 0x3f, 0x4c, 0xff, 0x43, 0x13, 0x08, 0x56, 0xf9, 0x7a, 0xfd, 0x65, 0x49,
-	0x2e, 0xa7, 0xde, 0xc5, 0x7b, 0x88, 0x98, 0xac, 0x1e, 0x41, 0xbe, 0xf2, 0xbe, 0x0e, 0x6d, 0xf1,
-	0xcb, 0x3f, 0xfd, 0x9c, 0x11, 0xda, 0xe2, 0xf9, 0x5e, 0xb1, 0xb2, 0x8a, 0xf5, 0x7e, 0x70, 0x73,
-	0x64, 0xef, 0x78, 0xfd, 0x27, 0x00, 0x00, 0xff, 0xff, 0x57, 0x01, 0x54, 0x95, 0xf7, 0x02, 0x00,
-	0x00,
+	// 470 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0x5d, 0x8b, 0xd3, 0x40,
+	0x14, 0x75, 0xb2, 0xb6, 0x4d, 0x6f, 0xbb, 0x52, 0x06, 0x59, 0x42, 0x51, 0x8c, 0x05, 0xb1, 0xec,
+	0xc3, 0x44, 0xe2, 0x8b, 0xb8, 0x28, 0xb4, 0xdd, 0x82, 0x82, 0x6c, 0xcb, 0x74, 0x55, 0xf0, 0x25,
+	0xcc, 0x4e, 0x46, 0x37, 0x6c, 0x32, 0x13, 0x66, 0x26, 0xd5, 0xfc, 0x25, 0xff, 0x9f, 0xef, 0x92,
+	0xaf, 0x65, 0x95, 0xee, 0xdb, 0xfd, 0x38, 0xf7, 0xcc, 0x3d, 0xe7, 0x0e, 0xbc, 0xdc, 0x87, 0x9a,
+	0x95, 0x84, 0xab, 0x2c, 0xe0, 0x4a, 0x8b, 0x20, 0xd7, 0xea, 0x57, 0x19, 0x18, 0xc5, 0x6f, 0x4c,
+	0xc0, 0x95, 0xfc, 0x9e, 0xfc, 0x20, 0xb9, 0x56, 0x56, 0xe1, 0x93, 0x0e, 0xa8, 0x05, 0xa9, 0x41,
+	0xa4, 0x06, 0x4d, 0xff, 0x27, 0xe0, 0x2a, 0xcb, 0x94, 0x0c, 0xa4, 0xb0, 0x01, 0x8b, 0x63, 0x2d,
+	0x8c, 0x69, 0x08, 0xa6, 0xaf, 0x0e, 0x03, 0xeb, 0x26, 0x57, 0x69, 0x60, 0x84, 0xde, 0x0b, 0x1d,
+	0x99, 0x5c, 0xf0, 0x66, 0x62, 0xb6, 0x80, 0xc1, 0x82, 0x73, 0x55, 0x48, 0x8b, 0xa7, 0xe0, 0x16,
+	0x46, 0x68, 0xc9, 0x32, 0xe1, 0x21, 0x1f, 0xcd, 0x87, 0xf4, 0x36, 0xaf, 0x7a, 0x39, 0x33, 0xe6,
+	0xa7, 0xd2, 0xb1, 0xe7, 0x34, 0xbd, 0x2e, 0x9f, 0xfd, 0x71, 0x60, 0xbc, 0xab, 0x89, 0x57, 0xb5,
+	0x18, 0xfc, 0x0e, 0x86, 0xac, 0xb0, 0xd7, 0x91, 0x2d, 0xf3, 0x86, 0xe9, 0x51, 0xe8, 0x93, 0xc3,
+	0xd2, 0xc8, 0xa2, 0xb0, 0xd7, 0x97, 0x65, 0x2e, 0xa8, 0xcb, 0xda, 0x08, 0x5f, 0x80, 0xcb, 0x9a,
+	0x95, 0x8c, 0xe7, 0xf8, 0x47, 0xf3, 0x51, 0x18, 0xde, 0x37, 0x7d, 0xf7, 0x59, 0xd2, 0xea, 0x30,
+	0x6b, 0x69, 0x75, 0x49, 0x6f, 0x39, 0xf0, 0x19, 0x0c, 0x5a, 0x97, 0xbc, 0x23, 0x1f, 0xcd, 0x47,
+	0xe1, 0xf3, 0xbb, 0x74, 0x8d, 0x45, 0x44, 0x0a, 0x4b, 0x3e, 0x6e, 0x37, 0xfa, 0x5c, 0x65, 0x2c,
+	0x91, 0xb4, 0x9b, 0xc0, 0xcf, 0x60, 0x54, 0xc4, 0x79, 0x24, 0x24, 0xbb, 0x4a, 0x45, 0xec, 0x3d,
+	0xf4, 0xd1, 0xdc, 0xa5, 0x50, 0xc4, 0xf9, 0xba, 0xa9, 0xe0, 0x27, 0x30, 0xb0, 0x49, 0x26, 0x54,
+	0x61, 0xbd, 0x9e, 0x8f, 0xe6, 0xc7, 0x4b, 0xc7, 0x43, 0xb4, 0x2b, 0xe1, 0xa7, 0x00, 0x95, 0x87,
+	0x51, 0x2a, 0xf6, 0x22, 0xf5, 0xfa, 0x15, 0x80, 0x0e, 0xab, 0xca, 0xa7, 0xaa, 0x30, 0x3d, 0x83,
+	0xe3, 0x7f, 0xb6, 0xc6, 0x13, 0x38, 0xba, 0x11, 0x65, 0x6b, 0x7f, 0x15, 0xe2, 0xc7, 0xd0, 0xdb,
+	0xb3, 0xb4, 0x10, 0xad, 0xed, 0x4d, 0xf2, 0xd6, 0x79, 0x83, 0x66, 0x14, 0xc6, 0xab, 0x34, 0x11,
+	0xd2, 0xb6, 0xb6, 0x2f, 0xa1, 0xdf, 0xdc, 0xd7, 0x43, 0xb5, 0x6b, 0xa7, 0x07, 0x64, 0x76, 0x3f,
+	0xa1, 0x75, 0x6e, 0x2d, 0xe3, 0x5c, 0x25, 0xd2, 0xd2, 0x76, 0xf2, 0xf4, 0x05, 0xb8, 0xdd, 0x45,
+	0xf0, 0x08, 0x06, 0x17, 0x9b, 0x68, 0xf1, 0xf9, 0xf2, 0xc3, 0xe4, 0x01, 0x1e, 0x83, 0xbb, 0x5d,
+	0xec, 0x76, 0x5f, 0x37, 0xf4, 0x7c, 0x82, 0x96, 0xef, 0x61, 0xca, 0x55, 0x76, 0xcf, 0x55, 0xb6,
+	0xe8, 0x5b, 0xaf, 0x0e, 0x7e, 0x3b, 0x27, 0x5f, 0x42, 0xca, 0x4a, 0xb2, 0xaa, 0x10, 0xdb, 0x1a,
+	0xb1, 0xab, 0x1a, 0x57, 0xfd, 0x7a, 0x8f, 0xd7, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x23, 0xac,
+	0x72, 0x71, 0x1a, 0x03, 0x00, 0x00,
 }

+ 3 - 2
proxy/socks/config.proto

@@ -24,9 +24,10 @@ message ServerConfig {
   map<string, string> accounts = 2;
   v2ray.core.common.net.IPOrDomain address = 3;
   bool udp_enabled = 4;
-  uint32 timeout = 5;
+  uint32 timeout = 5 [deprecated = true];
+  uint32 user_level = 6;
 }
 
 message ClientConfig {
   repeated v2ray.core.common.protocol.ServerEndpoint server = 1;
-}
+}

+ 17 - 7
proxy/socks/server.go

@@ -8,6 +8,7 @@ import (
 	"v2ray.com/core/app"
 	"v2ray.com/core/app/dispatcher"
 	"v2ray.com/core/app/log"
+	"v2ray.com/core/app/policy"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
 	"v2ray.com/core/common/net"
@@ -21,6 +22,7 @@ import (
 // Server is a SOCKS 5 proxy server
 type Server struct {
 	config *ServerConfig
+	policy policy.Policy
 }
 
 // NewServer creates a new Server object.
@@ -32,6 +34,17 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
 	s := &Server{
 		config: config,
 	}
+	space.OnInitialize(func() error {
+		pm := policy.PolicyFromSpace(space)
+		if pm == nil {
+			return newError("Policy not found in space.")
+		}
+		s.policy = pm.GetPolicy(config.UserLevel)
+		if config.Timeout > 0 && config.UserLevel == 0 {
+			s.policy.Timeout.ConnectionIdle.Value = config.Timeout
+		}
+		return nil
+	})
 	return s, nil
 }
 
@@ -57,7 +70,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet
 }
 
 func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
-	conn.SetReadDeadline(time.Now().Add(time.Second * 8))
+	conn.SetReadDeadline(time.Now().Add(s.policy.Timeout.Handshake.Duration()))
 	reader := buf.NewBufferedReader(buf.NewReader(conn))
 
 	inboundDest, ok := proxy.InboundEntryPointFromContext(ctx)
@@ -103,12 +116,8 @@ func (*Server) handleUDP(c net.Conn) error {
 }
 
 func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher dispatcher.Interface) error {
-	timeout := time.Second * time.Duration(v.config.Timeout)
-	if timeout == 0 {
-		timeout = time.Minute * 5
-	}
 	ctx, cancel := context.WithCancel(ctx)
-	timer := signal.CancelAfterInactivity(ctx, cancel, timeout)
+	timer := signal.CancelAfterInactivity(ctx, cancel, v.policy.Timeout.ConnectionIdle.Duration())
 
 	ray, err := dispatcher.Dispatch(ctx, dest)
 	if err != nil {
@@ -125,6 +134,7 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
 		if err := buf.Copy(v2reader, input, buf.UpdateActivity(timer)); err != nil {
 			return newError("failed to transport all TCP request").Base(err)
 		}
+		timer.SetTimeout(v.policy.Timeout.DownlinkOnly.Duration())
 		return nil
 	})
 
@@ -133,7 +143,7 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
 		if err := buf.Copy(output, v2writer, buf.UpdateActivity(timer)); err != nil {
 			return newError("failed to transport all TCP response").Base(err)
 		}
-		timer.SetTimeout(time.Second * 2)
+		timer.SetTimeout(v.policy.Timeout.UplinkOnly.Duration())
 		return nil
 	})
 

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

@@ -11,6 +11,7 @@ import (
 	"v2ray.com/core/app"
 	"v2ray.com/core/app/dispatcher"
 	"v2ray.com/core/app/log"
+	"v2ray.com/core/app/policy"
 	"v2ray.com/core/app/proxyman"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
@@ -78,6 +79,7 @@ type Handler struct {
 	usersByEmail          *userByEmail
 	detours               *DetourConfig
 	sessionHistory        *encoding.SessionHistory
+	policyManager         policy.Interface
 }
 
 // New creates a new VMess inbound handler.
@@ -104,7 +106,11 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
 	space.OnInitialize(func() error {
 		handler.inboundHandlerManager = proxyman.InboundHandlerManagerFromSpace(space)
 		if handler.inboundHandlerManager == nil {
-			return newError("InboundHandlerManager is not found is space")
+			return newError("InboundHandlerManager is not found is space.")
+		}
+		handler.policyManager = policy.PolicyFromSpace(space)
+		if handler.policyManager == nil {
+			return newError("Policy is not found in space.")
 		}
 		return nil
 	})
@@ -174,7 +180,8 @@ func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSess
 
 // Process implements proxy.Inbound.Process().
 func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher dispatcher.Interface) error {
-	if err := connection.SetReadDeadline(time.Now().Add(time.Second * 8)); err != nil {
+	sessionPolicy := h.policyManager.GetPolicy(0)
+	if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeout.Handshake.Duration())); err != nil {
 		return newError("unable to set read deadline").Base(err).AtWarning()
 	}
 
@@ -203,11 +210,11 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i
 		log.Trace(newError("unable to set back read deadline").Base(err))
 	}
 
-	userSettings := request.User.GetSettings()
+	sessionPolicy = h.policyManager.GetPolicy(request.User.Level)
 	ctx = protocol.ContextWithUser(ctx, request.User)
 
 	ctx, cancel := context.WithCancel(ctx)
-	timer := signal.CancelAfterInactivity(ctx, cancel, userSettings.PayloadTimeout)
+	timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration())
 	ray, err := dispatcher.Dispatch(ctx, request.Destination())
 	if err != nil {
 		return newError("failed to dispatch request to ", request.Destination()).Base(err)
@@ -217,12 +224,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i
 	output := ray.InboundOutput()
 
 	requestDone := signal.ExecuteAsync(func() error {
+		defer timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration())
 		return transferRequest(timer, session, request, reader, input)
 	})
 
 	responseDone := signal.ExecuteAsync(func() error {
 		writer := buf.NewBufferedWriter(buf.NewWriter(connection))
 		defer writer.Flush()
+		defer timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration())
 
 		response := &protocol.ResponseHeader{
 			Command: h.generateCommand(ctx, request),

+ 17 - 3
proxy/vmess/outbound/outbound.go

@@ -8,6 +8,7 @@ import (
 
 	"v2ray.com/core/app"
 	"v2ray.com/core/app/log"
+	"v2ray.com/core/app/policy"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
 	"v2ray.com/core/common/net"
@@ -23,8 +24,9 @@ import (
 
 // Handler is an outbound connection handler for VMess protocol.
 type Handler struct {
-	serverList   *protocol.ServerList
-	serverPicker protocol.ServerPicker
+	serverList    *protocol.ServerList
+	serverPicker  protocol.ServerPicker
+	policyManager policy.Interface
 }
 
 func New(ctx context.Context, config *Config) (*Handler, error) {
@@ -42,6 +44,15 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
 		serverPicker: protocol.NewRoundRobinServerPicker(serverList),
 	}
 
+	space.OnInitialize(func() error {
+		pm := policy.PolicyFromSpace(space)
+		if pm == nil {
+			return newError("Policy is not found in space.")
+		}
+		handler.policyManager = pm
+		return nil
+	})
+
 	return handler, nil
 }
 
@@ -102,9 +113,10 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
 	output := outboundRay.OutboundOutput()
 
 	session := encoding.NewClientSession(protocol.DefaultIDHash)
+	sessionPolicy := v.policyManager.GetPolicy(request.User.Level)
 
 	ctx, cancel := context.WithCancel(ctx)
-	timer := signal.CancelAfterInactivity(ctx, cancel, time.Minute*5)
+	timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration())
 
 	requestDone := signal.ExecuteAsync(func() error {
 		writer := buf.NewBufferedWriter(buf.NewWriter(conn))
@@ -137,11 +149,13 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
 				return err
 			}
 		}
+		timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration())
 		return nil
 	})
 
 	responseDone := signal.ExecuteAsync(func() error {
 		defer output.Close()
+		defer timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration())
 
 		reader := buf.NewBufferedReader(buf.NewReader(conn))
 		header, err := session.DecodeResponseHeader(reader)

+ 19 - 1
v2ray.go

@@ -7,6 +7,7 @@ import (
 	"v2ray.com/core/app/dispatcher"
 	"v2ray.com/core/app/dns"
 	"v2ray.com/core/app/log"
+	"v2ray.com/core/app/policy"
 	"v2ray.com/core/app/proxyman"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/net"
@@ -115,7 +116,24 @@ func newSimpleServer(config *Config) (*simpleServer, error) {
 			return nil, err
 		}
 		common.Must(space.AddApplication(d))
-		// disp = d.(dispatcher.Interface)
+	}
+
+	if p := policy.PolicyFromSpace(space); p == nil {
+		p, err := app.CreateAppFromConfig(ctx, &policy.Config{
+			Level: map[uint32]*policy.Policy{
+				1: &policy.Policy{
+					Timeout: &policy.Policy_Timeout{
+						ConnectionIdle: &policy.Second{
+							Value: 600,
+						},
+					},
+				},
+			},
+		})
+		if err != nil {
+			return nil, err
+		}
+		common.Must(space.AddApplication(p))
 	}
 
 	for _, inbound := range config.Inbound {