1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-09-27 22:36:12 -04:00

Merge pull request #1 from v2ray/master

Pull
This commit is contained in:
Ariselia 2018-02-11 18:05:21 +08:00 committed by GitHub
commit 693c9bda49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
130 changed files with 4323 additions and 2182 deletions

View File

@ -1,7 +1,7 @@
sudo: required
language: go
go:
- 1.9.2
- 1.9.4
go_import_path: v2ray.com/core
git:
depth: 5

View File

@ -1,4 +0,0 @@
package api
type ApiServer struct {
}

View File

@ -1,9 +0,0 @@
package api
import (
"v2ray.com/core/common/net"
)
type Config struct {
DirectPort net.Port
}

View File

@ -1,3 +1 @@
package app
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg app -path App

View File

@ -0,0 +1,95 @@
package commander
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg commander -path App,Commander
import (
"context"
"net"
"sync"
"google.golang.org/grpc"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/signal"
)
type Commander struct {
sync.Mutex
server *grpc.Server
config Config
v *core.Instance
ohm core.OutboundHandlerManager
}
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
c := &Commander{
config: *config,
ohm: v.OutboundHandlerManager(),
v: v,
}
if err := v.RegisterFeature((*core.Commander)(nil), c); err != nil {
return nil, err
}
return c, nil
}
func (c *Commander) Start() error {
c.Lock()
c.server = grpc.NewServer()
for _, rawConfig := range c.config.Service {
config, err := rawConfig.GetInstance()
if err != nil {
return err
}
rawService, err := c.v.CreateObject(config)
if err != nil {
return err
}
service, ok := rawService.(Service)
if !ok {
return newError("not a Service.")
}
service.Register(c.server)
}
c.Unlock()
listener := &OutboundListener{
buffer: make(chan net.Conn, 4),
done: signal.NewDone(),
}
go func() {
if err := c.server.Serve(listener); err != nil {
newError("failed to start grpc server").Base(err).AtError().WriteToLog()
}
}()
c.ohm.RemoveHandler(context.Background(), c.config.Tag)
c.ohm.AddHandler(context.Background(), &CommanderOutbound{
tag: c.config.Tag,
listener: listener,
})
return nil
}
func (c *Commander) Close() error {
c.Lock()
defer c.Unlock()
if c.server != nil {
c.server.Stop()
c.server = nil
}
return nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
return NewCommander(ctx, cfg.(*Config))
}))
}

View File

@ -0,0 +1,68 @@
package commander
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import v2ray_core_common_serial "v2ray.com/core/common/serial"
// 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
// Config is the settings for Commander.
type Config struct {
// Tag of the outbound handler that handles grpc connections.
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
// Services that supported by this server. All services must implement Service interface.
Service []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,rep,name=service" json:"service,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) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *Config) GetService() []*v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.Service
}
return nil
}
func init() {
proto.RegisterType((*Config)(nil), "v2ray.core.app.commander.Config")
}
func init() { proto.RegisterFile("v2ray.com/core/app/commander/config.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 212 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2c, 0x33, 0x2a, 0x4a,
0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0x4f,
0xce, 0xcf, 0xcd, 0x4d, 0xcc, 0x4b, 0x49, 0x2d, 0xd2, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7,
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x80, 0x29, 0x2d, 0x4a, 0xd5, 0x4b, 0x2c, 0x28, 0xd0,
0x83, 0x2b, 0x93, 0x32, 0x40, 0x33, 0x04, 0x24, 0x93, 0x9f, 0xa7, 0x5f, 0x9c, 0x5a, 0x94, 0x99,
0x98, 0xa3, 0x5f, 0x52, 0x59, 0x90, 0x9a, 0x12, 0x9f, 0x9b, 0x5a, 0x5c, 0x9c, 0x98, 0x9e, 0x0a,
0x31, 0x4b, 0x29, 0x86, 0x8b, 0xcd, 0x19, 0x6c, 0xb6, 0x90, 0x00, 0x17, 0x73, 0x49, 0x62, 0xba,
0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x88, 0x29, 0xe4, 0xc0, 0xc5, 0x5e, 0x9c, 0x5a, 0x54,
0x96, 0x99, 0x9c, 0x2a, 0xc1, 0xa4, 0xc0, 0xac, 0xc1, 0x6d, 0xa4, 0xa6, 0x87, 0x64, 0x33, 0xc4,
0x6c, 0x3d, 0x88, 0xd9, 0x7a, 0x21, 0x20, 0xb3, 0x7d, 0x21, 0x46, 0x07, 0xc1, 0xb4, 0x39, 0xb9,
0x71, 0xc9, 0x24, 0xe7, 0xe7, 0xea, 0xe1, 0x72, 0x6f, 0x00, 0x63, 0x14, 0x27, 0x9c, 0xb3, 0x8a,
0x49, 0x22, 0xcc, 0x28, 0x28, 0xb1, 0x52, 0xcf, 0x19, 0xa4, 0xce, 0xb1, 0xa0, 0x40, 0xcf, 0x19,
0x26, 0x95, 0xc4, 0x06, 0x76, 0xac, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x29, 0x02, 0xa6, 0x19,
0x25, 0x01, 0x00, 0x00,
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
package v2ray.core.app.commander;
option csharp_namespace = "V2Ray.Core.App.Commander";
option go_package = "commander";
option java_package = "com.v2ray.core.app.commander";
option java_multiple_files = true;
import "v2ray.com/core/common/serial/typed_message.proto";
// Config is the settings for Commander.
message Config {
// Tag of the outbound handler that handles grpc connections.
string tag = 1;
// Services that supported by this server. All services must implement Service interface.
repeated v2ray.core.common.serial.TypedMessage service = 2;
}

View File

@ -1,7 +1,7 @@
package impl
package commander
import "v2ray.com/core/common/errors"
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).Path("App", "Dispatcher", "Default")
return errors.New(values...).Path("App", "Commander")
}

101
app/commander/outbound.go Normal file
View File

@ -0,0 +1,101 @@
package commander
import (
"context"
"net"
"sync"
"v2ray.com/core/common/signal"
"v2ray.com/core/transport/ray"
)
type OutboundListener struct {
buffer chan net.Conn
done *signal.Done
}
func (l *OutboundListener) add(conn net.Conn) {
select {
case l.buffer <- conn:
case <-l.done.C():
conn.Close()
default:
conn.Close()
}
}
func (l *OutboundListener) Accept() (net.Conn, error) {
select {
case <-l.done.C():
return nil, newError("listern closed")
case c := <-l.buffer:
return c, nil
}
}
func (l *OutboundListener) Close() error {
l.done.Close()
L:
for {
select {
case c := <-l.buffer:
c.Close()
default:
break L
}
}
return nil
}
func (l *OutboundListener) Addr() net.Addr {
return &net.TCPAddr{
IP: net.IP{0, 0, 0, 0},
Port: 0,
}
}
type CommanderOutbound struct {
tag string
listener *OutboundListener
access sync.RWMutex
closed bool
}
func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) {
co.access.RLock()
if co.closed {
r.OutboundInput().CloseError()
r.OutboundOutput().CloseError()
co.access.RUnlock()
return
}
closeSignal := signal.NewNotifier()
c := ray.NewConnection(r.OutboundInput(), r.OutboundOutput(), ray.ConnCloseSignal(closeSignal))
co.listener.add(c)
co.access.RUnlock()
<-closeSignal.Wait()
return
}
func (co *CommanderOutbound) Tag() string {
return co.tag
}
func (co *CommanderOutbound) Start() error {
co.access.Lock()
co.closed = false
co.access.Unlock()
return nil
}
func (co *CommanderOutbound) Close() error {
co.access.Lock()
co.closed = true
co.listener.Close()
co.access.Unlock()
return nil
}

9
app/commander/service.go Normal file
View File

@ -0,0 +1,9 @@
package commander
import (
"google.golang.org/grpc"
)
type Service interface {
Register(*grpc.Server)
}

View File

@ -1,4 +1,4 @@
package impl
package dispatcher
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg impl -path App,Dispatcher,Default
@ -6,10 +6,8 @@ import (
"context"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/app/router"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
@ -21,31 +19,27 @@ var (
errSniffingTimeout = newError("timeout on sniffing")
)
var (
_ app.Application = (*DefaultDispatcher)(nil)
)
// DefaultDispatcher is a default implementation of Dispatcher.
type DefaultDispatcher struct {
ohm proxyman.OutboundHandlerManager
router *router.Router
ohm core.OutboundHandlerManager
router core.Router
}
// NewDefaultDispatcher create a new DefaultDispatcher.
func NewDefaultDispatcher(ctx context.Context, config *dispatcher.Config) (*DefaultDispatcher, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context")
func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatcher, error) {
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
d := &DefaultDispatcher{
ohm: v.OutboundHandlerManager(),
router: v.Router(),
}
if err := v.RegisterFeature((*core.Dispatcher)(nil), d); err != nil {
return nil, newError("unable to register Dispatcher")
}
d := &DefaultDispatcher{}
space.On(app.SpaceInitializing, func(interface{}) error {
d.ohm = proxyman.OutboundHandlerManagerFromSpace(space)
if d.ohm == nil {
return newError("OutboundHandlerManager is not found in the space")
}
d.router = router.FromSpace(space)
return nil
})
return d, nil
}
@ -55,14 +49,9 @@ func (*DefaultDispatcher) Start() error {
}
// Close implements app.Application.
func (*DefaultDispatcher) Close() {}
func (*DefaultDispatcher) Close() error { return nil }
// Interface implements app.Application.
func (*DefaultDispatcher) Interface() interface{} {
return (*dispatcher.Interface)(nil)
}
// Dispatch implements Dispatcher.Interface.
// Dispatch implements core.Dispatcher.
func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) {
if !destination.IsValid() {
panic("Dispatcher: Invalid destination.")
@ -120,7 +109,7 @@ func snifer(ctx context.Context, sniferList []proxyman.KnownProtocols, outbound
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.OutboundRay, destination net.Destination) {
dispatcher := d.ohm.GetDefaultHandler()
if d.router != nil {
if tag, err := d.router.TakeDetour(ctx); err == nil {
if tag, err := d.router.PickRoute(ctx); err == nil {
if handler := d.ohm.GetHandler(tag); handler != nil {
newError("taking detour [", tag, "] for [", destination, "]").WriteToLog()
dispatcher = handler
@ -135,7 +124,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.Out
}
func init() {
common.Must(common.RegisterConfig((*dispatcher.Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewDefaultDispatcher(ctx, config.(*dispatcher.Config))
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewDefaultDispatcher(ctx, config.(*Config))
}))
}

View File

@ -1,21 +1,3 @@
package dispatcher
import (
"context"
"v2ray.com/core/app"
"v2ray.com/core/common/net"
"v2ray.com/core/transport/ray"
)
// Interface dispatch a packet and possibly further network payload to its destination.
type Interface interface {
Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error)
}
func FromSpace(space app.Space) Interface {
if app := space.GetApplication((*Interface)(nil)); app != nil {
return app.(Interface)
}
return nil
}
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg dispatcher -path App,Dispatcher

View File

@ -0,0 +1,7 @@
package dispatcher
import "v2ray.com/core/common/errors"
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).Path("App", "Dispatcher")
}

View File

@ -1,4 +1,4 @@
package impl
package dispatcher
import (
"bytes"

View File

@ -1,11 +1,10 @@
package impl_test
package dispatcher_test
import (
"testing"
. "v2ray.com/core/app/dispatcher"
"v2ray.com/core/app/proxyman"
. "v2ray.com/core/app/dispatcher/impl"
. "v2ray.com/ext/assert"
)

View File

@ -6,7 +6,7 @@ import (
"time"
"github.com/miekg/dns"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/dice"
"v2ray.com/core/common/net"
@ -48,7 +48,7 @@ type UDPNameServer struct {
nextCleanup time.Time
}
func NewUDPNameServer(address net.Destination, dispatcher dispatcher.Interface) *UDPNameServer {
func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPNameServer {
s := &UDPNameServer{
address: address,
requests: make(map[uint16]*PendingRequest),
@ -216,8 +216,7 @@ func (*LocalNameServer) QueryA(domain string) <-chan *ARecord {
go func() {
defer close(response)
resolver := net.SystemIPResolver()
ips, err := resolver.LookupIP(domain)
ips, err := net.LookupIP(domain)
if err != nil {
newError("failed to lookup IPs for domain ", domain).Base(err).AtWarning().WriteToLog()
return

View File

@ -1,6 +1,6 @@
package dns
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg server -path App,DNS,Server
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg dns -path App,DNS
import (
"context"
@ -8,10 +8,10 @@ import (
"time"
dnsmsg "github.com/miekg/dns"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/net"
"v2ray.com/core/common/signal"
)
const (
@ -38,56 +38,60 @@ type Server struct {
hosts map[string]net.IP
records map[string]*DomainRecord
servers []NameServer
task *signal.PeriodicTask
}
func New(ctx context.Context, config *Config) (*Server, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context")
}
server := &Server{
records: make(map[string]*DomainRecord),
servers: make([]NameServer, len(config.NameServers)),
hosts: config.GetInternalHosts(),
}
space.On(app.SpaceInitializing, func(interface{}) error {
disp := dispatcher.FromSpace(space)
if disp == nil {
return newError("dispatcher is not found in the space")
}
for idx, destPB := range config.NameServers {
address := destPB.Address.AsAddress()
if address.Family().IsDomain() && address.Domain() == "localhost" {
server.servers[idx] = &LocalNameServer{}
} else {
dest := destPB.AsDestination()
if dest.Network == net.Network_Unknown {
dest.Network = net.Network_UDP
}
if dest.Network == net.Network_UDP {
server.servers[idx] = NewUDPNameServer(dest, disp)
}
server.task = &signal.PeriodicTask{
Interval: time.Minute * 10,
Execute: func() error {
server.cleanup()
return nil
},
}
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
if err := v.RegisterFeature((*core.DNSClient)(nil), server); err != nil {
return nil, newError("unable to register DNSClient.").Base(err)
}
for idx, destPB := range config.NameServers {
address := destPB.Address.AsAddress()
if address.Family().IsDomain() && address.Domain() == "localhost" {
server.servers[idx] = &LocalNameServer{}
} else {
dest := destPB.AsDestination()
if dest.Network == net.Network_Unknown {
dest.Network = net.Network_UDP
}
if dest.Network == net.Network_UDP {
server.servers[idx] = NewUDPNameServer(dest, v.Dispatcher())
}
}
if len(config.NameServers) == 0 {
server.servers = append(server.servers, &LocalNameServer{})
}
return nil
})
}
if len(config.NameServers) == 0 {
server.servers = append(server.servers, &LocalNameServer{})
}
return server, nil
}
func (*Server) Interface() interface{} {
return (*Server)(nil)
}
// Start implements common.Runnable.
func (s *Server) Start() error {
net.RegisterIPResolver(s)
return nil
return s.task.Start()
}
func (*Server) Close() {
net.RegisterIPResolver(net.SystemIPResolver())
// Close implements common.Runnable.
func (s *Server) Close() error {
return s.task.Close()
}
func (s *Server) GetCached(domain string) []net.IP {
@ -101,18 +105,12 @@ func (s *Server) GetCached(domain string) []net.IP {
return nil
}
func (s *Server) tryCleanup() {
func (s *Server) cleanup() {
s.Lock()
defer s.Unlock()
if len(s.records) > 256 {
domains := make([]string, 0, 256)
for d, r := range s.records {
if r.Expired() {
domains = append(domains, d)
}
}
for _, d := range domains {
for d, r := range s.records {
if r.Expired() {
delete(s.records, d)
}
}
@ -129,8 +127,6 @@ func (s *Server) LookupIP(domain string) ([]net.IP, error) {
return ips, nil
}
s.tryCleanup()
for _, server := range s.servers {
response := server.QueryA(domain)
select {

View File

@ -1,18 +1,14 @@
package dns_test
import (
"context"
"testing"
"v2ray.com/core/app"
"v2ray.com/core"
"v2ray.com/core/app/dispatcher"
_ "v2ray.com/core/app/dispatcher/impl"
. "v2ray.com/core/app/dns"
"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"
"v2ray.com/core/common/net"
"v2ray.com/core/common/serial"
"v2ray.com/core/proxy/freedom"
@ -54,50 +50,50 @@ func TestUDPServer(t *testing.T) {
go dnsServer.ListenAndServe()
config := &Config{
NameServers: []*net.Endpoint{
{
Network: net.Network_UDP,
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
config := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{
NameServers: []*net.Endpoint{
{
Network: net.Network_UDP,
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
},
Port: uint32(port),
},
},
Port: uint32(port),
}),
serial.ToTypedMessage(&dispatcher.Config{}),
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
serial.ToTypedMessage(&policy.Config{}),
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
ctx := context.Background()
space := app.NewSpace()
v, err := core.New(config)
assert(err, IsNil)
ctx = app.ContextWithSpace(ctx, space)
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{}))
client := v.DNSClient()
om := proxyman.OutboundHandlerManagerFromSpace(space)
om.AddHandler(ctx, &proxyman.OutboundHandlerConfig{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
})
common.Must(space.Initialize())
common.Must(space.Start())
ips, err := net.LookupIP("google.com")
ips, err := client.LookupIP("google.com")
assert(err, IsNil)
assert(len(ips), Equals, 1)
assert([]byte(ips[0]), Equals, []byte{8, 8, 8, 8})
ips, err = net.LookupIP("facebook.com")
ips, err = client.LookupIP("facebook.com")
assert(err, IsNil)
assert(len(ips), Equals, 1)
assert([]byte(ips[0]), Equals, []byte{9, 9, 9, 9})
dnsServer.Shutdown()
ips, err = net.LookupIP("google.com")
ips, err = client.LookupIP("google.com")
assert(err, IsNil)
assert(len(ips), Equals, 1)
assert([]byte(ips[0]), Equals, []byte{8, 8, 8, 8})

View File

@ -37,11 +37,6 @@ func New(ctx context.Context, config *Config) (*Instance, error) {
return g, nil
}
// Interface implements app.Application.Interface().
func (*Instance) Interface() interface{} {
return (*Instance)(nil)
}
func (g *Instance) initAccessLogger() error {
switch g.config.AccessLogType {
case LogType_File:
@ -108,11 +103,13 @@ func (g *Instance) Handle(msg log.Message) {
}
// Close implement app.Application.Close().
func (g *Instance) Close() {
func (g *Instance) Close() error {
g.Lock()
defer g.Unlock()
g.active = false
return nil
}
func init() {

View File

@ -2,10 +2,15 @@ package policy
import (
"time"
"v2ray.com/core"
)
// Duration converts Second to time.Duration.
func (s *Second) Duration() time.Duration {
if s == nil {
return 0
}
return time.Second * time.Duration(s.Value)
}
@ -26,3 +31,14 @@ func (p *Policy) OverrideWith(another *Policy) {
}
}
}
func (p *Policy) ToCorePolicy() core.Policy {
var cp core.Policy
if p.Timeout != nil {
cp.Timeouts.ConnectionIdle = p.Timeout.ConnectionIdle.Duration()
cp.Timeouts.Handshake = p.Timeout.Handshake.Duration()
cp.Timeouts.DownlinkOnly = p.Timeout.DownlinkOnly.Duration()
cp.Timeouts.UplinkOnly = p.Timeout.UplinkOnly.Duration()
}
return cp
}

View File

@ -1,5 +1,5 @@
package app
package policy
import "v2ray.com/core/common/errors"
func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App") }
func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Policy") }

62
app/policy/manager.go Normal file
View File

@ -0,0 +1,62 @@
package policy
import (
"context"
"v2ray.com/core"
"v2ray.com/core/common"
)
// Instance is an instance of Policy manager.
type Instance struct {
levels map[uint32]core.Policy
}
// New creates new Policy manager instance.
func New(ctx context.Context, config *Config) (*Instance, error) {
m := &Instance{
levels: make(map[uint32]core.Policy),
}
if len(config.Level) > 0 {
for lv, p := range config.Level {
dp := core.DefaultPolicy()
dp.OverrideWith(p.ToCorePolicy())
m.levels[lv] = dp
}
}
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
if err := v.RegisterFeature((*core.PolicyManager)(nil), m); err != nil {
return nil, newError("unable to register PolicyManager in core").Base(err).AtError()
}
return m, nil
}
// ForLevel implements core.PolicyManager.
func (m *Instance) ForLevel(level uint32) core.Policy {
if p, ok := m.levels[level]; ok {
return p
}
return core.DefaultPolicy()
}
// Start implements app.Application.Start().
func (m *Instance) Start() error {
return nil
}
// Close implements app.Application.Close().
func (m *Instance) Close() error {
return nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*Config))
}))
}

View File

@ -1,68 +0,0 @@
package manager
import (
"context"
"v2ray.com/core/app/policy"
"v2ray.com/core/common"
)
// Instance is an instance of Policy manager.
type Instance struct {
levels map[uint32]*policy.Policy
}
// New creates new Policy manager instance.
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},
},
}
}
// GetPolicy implements policy.Manager.
func (m *Instance) GetPolicy(level uint32) policy.Policy {
if p, ok := m.levels[level]; ok {
return *p
}
return global()
}
// Start implements app.Application.Start().
func (m *Instance) Start() error {
return nil
}
// Close implements app.Application.Close().
func (m *Instance) Close() {
}
// Interface implement app.Application.Interface().
func (m *Instance) Interface() interface{} {
return (*policy.Manager)(nil)
}
func init() {
common.Must(common.RegisterConfig((*policy.Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*policy.Config))
}))
}

View File

@ -1,20 +1,3 @@
package policy
import (
"v2ray.com/core/app"
)
// Manager is an utility to manage policy per user level.
type Manager interface {
// GetPolicy returns the Policy for the given user level.
GetPolicy(level uint32) Policy
}
// FromSpace returns the policy.Manager in a space.
func FromSpace(space app.Space) Manager {
app := space.GetApplication((*Manager)(nil))
if app == nil {
return nil
}
return app.(Manager)
}
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg policy -path App,Policy

View File

@ -0,0 +1,148 @@
package command
import (
"context"
grpc "google.golang.org/grpc"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/proxy"
)
// InboundOperation is the interface for operations that applies to inbound handlers.
type InboundOperation interface {
// ApplyInbound appliess this operation to the given inbound handler.
ApplyInbound(context.Context, core.InboundHandler) error
}
// OutboundOperation is the interface for operations that applies to outbound handlers.
type OutboundOperation interface {
// ApplyOutbound applies this operation to the given outbound handler.
ApplyOutbound(context.Context, core.OutboundHandler) error
}
func getInbound(handler core.InboundHandler) (proxy.Inbound, error) {
gi, ok := handler.(proxy.GetInbound)
if !ok {
return nil, newError("can't get inbound proxy from handler.")
}
return gi.GetInbound(), nil
}
// ApplyInbound implements InboundOperation.
func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler core.InboundHandler) error {
p, err := getInbound(handler)
if err != nil {
return err
}
um, ok := p.(proxy.UserManager)
if !ok {
return newError("proxy is not an UserManager")
}
return um.AddUser(ctx, op.User)
}
// ApplyInbound implements InboundOperation.
func (op *RemoveUserOperation) ApplyInbound(ctx context.Context, handler core.InboundHandler) error {
p, err := getInbound(handler)
if err != nil {
return err
}
um, ok := p.(proxy.UserManager)
if !ok {
return newError("proxy is not an UserManager")
}
return um.RemoveUser(ctx, op.Email)
}
type handlerServer struct {
s *core.Instance
ihm core.InboundHandlerManager
ohm core.OutboundHandlerManager
}
func (s *handlerServer) AddInbound(ctx context.Context, request *AddInboundRequest) (*AddInboundResponse, error) {
rawHandler, err := s.s.CreateObject(request.Inbound)
if err != nil {
return nil, err
}
handler, ok := rawHandler.(core.InboundHandler)
if !ok {
return nil, newError("not an InboundHandler.")
}
return &AddInboundResponse{}, s.ihm.AddHandler(ctx, handler)
}
func (s *handlerServer) RemoveInbound(ctx context.Context, request *RemoveInboundRequest) (*RemoveInboundResponse, error) {
return &RemoveInboundResponse{}, s.ihm.RemoveHandler(ctx, request.Tag)
}
func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundRequest) (*AlterInboundResponse, error) {
rawOperation, err := request.Operation.GetInstance()
if err != nil {
return nil, newError("unknown operation").Base(err)
}
operation, ok := rawOperation.(InboundOperation)
if !ok {
return nil, newError("not an inbound operation")
}
handler, err := s.ihm.GetHandler(ctx, request.Tag)
if err != nil {
return nil, newError("failed to get handler: ", request.Tag).Base(err)
}
return &AlterInboundResponse{}, operation.ApplyInbound(ctx, handler)
}
func (s *handlerServer) AddOutbound(ctx context.Context, request *AddOutboundRequest) (*AddOutboundResponse, error) {
rawHandler, err := s.s.CreateObject(request.Outbound)
if err != nil {
return nil, err
}
handler, ok := rawHandler.(core.OutboundHandler)
if !ok {
return nil, newError("not an OutboundHandler.")
}
return &AddOutboundResponse{}, s.ohm.AddHandler(ctx, handler)
}
func (s *handlerServer) RemoveOutbound(ctx context.Context, request *RemoveOutboundRequest) (*RemoveOutboundResponse, error) {
return &RemoveOutboundResponse{}, s.ohm.RemoveHandler(ctx, request.Tag)
}
func (s *handlerServer) AlterOutbound(ctx context.Context, request *AlterOutboundRequest) (*AlterOutboundResponse, error) {
rawOperation, err := request.Operation.GetInstance()
if err != nil {
return nil, newError("unknown operation").Base(err)
}
operation, ok := rawOperation.(OutboundOperation)
if !ok {
return nil, newError("not an outbound operation")
}
handler := s.ohm.GetHandler(request.Tag)
return &AlterOutboundResponse{}, operation.ApplyOutbound(ctx, handler)
}
type service struct {
v *core.Instance
}
func (s *service) Register(server *grpc.Server) {
RegisterHandlerServiceServer(server, &handlerServer{
s: s.v,
ihm: s.v.InboundHandlerManager(),
ohm: s.v.OutboundHandlerManager(),
})
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
s := core.FromContext(ctx)
if s == nil {
return nil, newError("V is not in context.")
}
return &service{v: s}, nil
}))
}

View File

@ -0,0 +1,520 @@
package command
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import v2ray_core_common_protocol "v2ray.com/core/common/protocol"
import v2ray_core_common_serial "v2ray.com/core/common/serial"
import v2ray_core "v2ray.com/core"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// 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 AddUserOperation struct {
User *v2ray_core_common_protocol.User `protobuf:"bytes,1,opt,name=user" json:"user,omitempty"`
}
func (m *AddUserOperation) Reset() { *m = AddUserOperation{} }
func (m *AddUserOperation) String() string { return proto.CompactTextString(m) }
func (*AddUserOperation) ProtoMessage() {}
func (*AddUserOperation) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *AddUserOperation) GetUser() *v2ray_core_common_protocol.User {
if m != nil {
return m.User
}
return nil
}
type RemoveUserOperation struct {
Email string `protobuf:"bytes,1,opt,name=email" json:"email,omitempty"`
}
func (m *RemoveUserOperation) Reset() { *m = RemoveUserOperation{} }
func (m *RemoveUserOperation) String() string { return proto.CompactTextString(m) }
func (*RemoveUserOperation) ProtoMessage() {}
func (*RemoveUserOperation) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *RemoveUserOperation) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
type AddInboundRequest struct {
Inbound *v2ray_core.InboundHandlerConfig `protobuf:"bytes,1,opt,name=inbound" json:"inbound,omitempty"`
}
func (m *AddInboundRequest) Reset() { *m = AddInboundRequest{} }
func (m *AddInboundRequest) String() string { return proto.CompactTextString(m) }
func (*AddInboundRequest) ProtoMessage() {}
func (*AddInboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *AddInboundRequest) GetInbound() *v2ray_core.InboundHandlerConfig {
if m != nil {
return m.Inbound
}
return nil
}
type AddInboundResponse struct {
}
func (m *AddInboundResponse) Reset() { *m = AddInboundResponse{} }
func (m *AddInboundResponse) String() string { return proto.CompactTextString(m) }
func (*AddInboundResponse) ProtoMessage() {}
func (*AddInboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
type RemoveInboundRequest struct {
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
}
func (m *RemoveInboundRequest) Reset() { *m = RemoveInboundRequest{} }
func (m *RemoveInboundRequest) String() string { return proto.CompactTextString(m) }
func (*RemoveInboundRequest) ProtoMessage() {}
func (*RemoveInboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *RemoveInboundRequest) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
type RemoveInboundResponse struct {
}
func (m *RemoveInboundResponse) Reset() { *m = RemoveInboundResponse{} }
func (m *RemoveInboundResponse) String() string { return proto.CompactTextString(m) }
func (*RemoveInboundResponse) ProtoMessage() {}
func (*RemoveInboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
type AlterInboundRequest struct {
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
Operation *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=operation" json:"operation,omitempty"`
}
func (m *AlterInboundRequest) Reset() { *m = AlterInboundRequest{} }
func (m *AlterInboundRequest) String() string { return proto.CompactTextString(m) }
func (*AlterInboundRequest) ProtoMessage() {}
func (*AlterInboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *AlterInboundRequest) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *AlterInboundRequest) GetOperation() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.Operation
}
return nil
}
type AlterInboundResponse struct {
}
func (m *AlterInboundResponse) Reset() { *m = AlterInboundResponse{} }
func (m *AlterInboundResponse) String() string { return proto.CompactTextString(m) }
func (*AlterInboundResponse) ProtoMessage() {}
func (*AlterInboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
type AddOutboundRequest struct {
Outbound *v2ray_core.OutboundHandlerConfig `protobuf:"bytes,1,opt,name=outbound" json:"outbound,omitempty"`
}
func (m *AddOutboundRequest) Reset() { *m = AddOutboundRequest{} }
func (m *AddOutboundRequest) String() string { return proto.CompactTextString(m) }
func (*AddOutboundRequest) ProtoMessage() {}
func (*AddOutboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *AddOutboundRequest) GetOutbound() *v2ray_core.OutboundHandlerConfig {
if m != nil {
return m.Outbound
}
return nil
}
type AddOutboundResponse struct {
}
func (m *AddOutboundResponse) Reset() { *m = AddOutboundResponse{} }
func (m *AddOutboundResponse) String() string { return proto.CompactTextString(m) }
func (*AddOutboundResponse) ProtoMessage() {}
func (*AddOutboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
type RemoveOutboundRequest struct {
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
}
func (m *RemoveOutboundRequest) Reset() { *m = RemoveOutboundRequest{} }
func (m *RemoveOutboundRequest) String() string { return proto.CompactTextString(m) }
func (*RemoveOutboundRequest) ProtoMessage() {}
func (*RemoveOutboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
func (m *RemoveOutboundRequest) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
type RemoveOutboundResponse struct {
}
func (m *RemoveOutboundResponse) Reset() { *m = RemoveOutboundResponse{} }
func (m *RemoveOutboundResponse) String() string { return proto.CompactTextString(m) }
func (*RemoveOutboundResponse) ProtoMessage() {}
func (*RemoveOutboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
type AlterOutboundRequest struct {
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
Operation *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=operation" json:"operation,omitempty"`
}
func (m *AlterOutboundRequest) Reset() { *m = AlterOutboundRequest{} }
func (m *AlterOutboundRequest) String() string { return proto.CompactTextString(m) }
func (*AlterOutboundRequest) ProtoMessage() {}
func (*AlterOutboundRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (m *AlterOutboundRequest) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *AlterOutboundRequest) GetOperation() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.Operation
}
return nil
}
type AlterOutboundResponse struct {
}
func (m *AlterOutboundResponse) Reset() { *m = AlterOutboundResponse{} }
func (m *AlterOutboundResponse) String() string { return proto.CompactTextString(m) }
func (*AlterOutboundResponse) ProtoMessage() {}
func (*AlterOutboundResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
type Config struct {
}
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{14} }
func init() {
proto.RegisterType((*AddUserOperation)(nil), "v2ray.core.app.proxyman.command.AddUserOperation")
proto.RegisterType((*RemoveUserOperation)(nil), "v2ray.core.app.proxyman.command.RemoveUserOperation")
proto.RegisterType((*AddInboundRequest)(nil), "v2ray.core.app.proxyman.command.AddInboundRequest")
proto.RegisterType((*AddInboundResponse)(nil), "v2ray.core.app.proxyman.command.AddInboundResponse")
proto.RegisterType((*RemoveInboundRequest)(nil), "v2ray.core.app.proxyman.command.RemoveInboundRequest")
proto.RegisterType((*RemoveInboundResponse)(nil), "v2ray.core.app.proxyman.command.RemoveInboundResponse")
proto.RegisterType((*AlterInboundRequest)(nil), "v2ray.core.app.proxyman.command.AlterInboundRequest")
proto.RegisterType((*AlterInboundResponse)(nil), "v2ray.core.app.proxyman.command.AlterInboundResponse")
proto.RegisterType((*AddOutboundRequest)(nil), "v2ray.core.app.proxyman.command.AddOutboundRequest")
proto.RegisterType((*AddOutboundResponse)(nil), "v2ray.core.app.proxyman.command.AddOutboundResponse")
proto.RegisterType((*RemoveOutboundRequest)(nil), "v2ray.core.app.proxyman.command.RemoveOutboundRequest")
proto.RegisterType((*RemoveOutboundResponse)(nil), "v2ray.core.app.proxyman.command.RemoveOutboundResponse")
proto.RegisterType((*AlterOutboundRequest)(nil), "v2ray.core.app.proxyman.command.AlterOutboundRequest")
proto.RegisterType((*AlterOutboundResponse)(nil), "v2ray.core.app.proxyman.command.AlterOutboundResponse")
proto.RegisterType((*Config)(nil), "v2ray.core.app.proxyman.command.Config")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for HandlerService service
type HandlerServiceClient interface {
AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error)
RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error)
AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error)
AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error)
RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error)
AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error)
}
type handlerServiceClient struct {
cc *grpc.ClientConn
}
func NewHandlerServiceClient(cc *grpc.ClientConn) HandlerServiceClient {
return &handlerServiceClient{cc}
}
func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) {
out := new(AddInboundResponse)
err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/AddInbound", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) {
out := new(RemoveInboundResponse)
err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/RemoveInbound", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) {
out := new(AlterInboundResponse)
err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/AlterInbound", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) {
out := new(AddOutboundResponse)
err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/AddOutbound", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) {
out := new(RemoveOutboundResponse)
err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/RemoveOutbound", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *handlerServiceClient) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) {
out := new(AlterOutboundResponse)
err := grpc.Invoke(ctx, "/v2ray.core.app.proxyman.command.HandlerService/AlterOutbound", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for HandlerService service
type HandlerServiceServer interface {
AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error)
RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error)
AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error)
AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error)
RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error)
AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error)
}
func RegisterHandlerServiceServer(s *grpc.Server, srv HandlerServiceServer) {
s.RegisterService(&_HandlerService_serviceDesc, srv)
}
func _HandlerService_AddInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddInboundRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HandlerServiceServer).AddInbound(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/AddInbound",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).AddInbound(ctx, req.(*AddInboundRequest))
}
return interceptor(ctx, in, info, handler)
}
func _HandlerService_RemoveInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveInboundRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HandlerServiceServer).RemoveInbound(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/RemoveInbound",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).RemoveInbound(ctx, req.(*RemoveInboundRequest))
}
return interceptor(ctx, in, info, handler)
}
func _HandlerService_AlterInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AlterInboundRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HandlerServiceServer).AlterInbound(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/AlterInbound",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).AlterInbound(ctx, req.(*AlterInboundRequest))
}
return interceptor(ctx, in, info, handler)
}
func _HandlerService_AddOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddOutboundRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HandlerServiceServer).AddOutbound(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/AddOutbound",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).AddOutbound(ctx, req.(*AddOutboundRequest))
}
return interceptor(ctx, in, info, handler)
}
func _HandlerService_RemoveOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveOutboundRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HandlerServiceServer).RemoveOutbound(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/RemoveOutbound",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).RemoveOutbound(ctx, req.(*RemoveOutboundRequest))
}
return interceptor(ctx, in, info, handler)
}
func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AlterOutboundRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HandlerServiceServer).AlterOutbound(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/v2ray.core.app.proxyman.command.HandlerService/AlterOutbound",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).AlterOutbound(ctx, req.(*AlterOutboundRequest))
}
return interceptor(ctx, in, info, handler)
}
var _HandlerService_serviceDesc = grpc.ServiceDesc{
ServiceName: "v2ray.core.app.proxyman.command.HandlerService",
HandlerType: (*HandlerServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "AddInbound",
Handler: _HandlerService_AddInbound_Handler,
},
{
MethodName: "RemoveInbound",
Handler: _HandlerService_RemoveInbound_Handler,
},
{
MethodName: "AlterInbound",
Handler: _HandlerService_AlterInbound_Handler,
},
{
MethodName: "AddOutbound",
Handler: _HandlerService_AddOutbound_Handler,
},
{
MethodName: "RemoveOutbound",
Handler: _HandlerService_RemoveOutbound_Handler,
},
{
MethodName: "AlterOutbound",
Handler: _HandlerService_AlterOutbound_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "v2ray.com/core/app/proxyman/command/command.proto",
}
func init() { proto.RegisterFile("v2ray.com/core/app/proxyman/command/command.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 557 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xdf, 0x6b, 0xd3, 0x40,
0x1c, 0xb7, 0x53, 0xbb, 0xed, 0x3b, 0x1d, 0xf3, 0xda, 0x6e, 0x25, 0x3e, 0x6c, 0x46, 0x90, 0x0d,
0xe1, 0xa2, 0x59, 0x37, 0x41, 0xf0, 0xa1, 0xd6, 0x87, 0xf9, 0x20, 0x1d, 0x99, 0xfa, 0xe0, 0x8b,
0xdc, 0x92, 0xb3, 0x04, 0x92, 0xbb, 0xf3, 0x92, 0x56, 0x2b, 0x08, 0x82, 0xff, 0x80, 0x7f, 0x87,
0x7f, 0xa5, 0x24, 0x77, 0xd7, 0x25, 0x69, 0x21, 0x0d, 0xf8, 0xd4, 0xf4, 0xfa, 0xf9, 0xf5, 0xfd,
0xde, 0x27, 0x14, 0x9e, 0xcf, 0x5c, 0x49, 0xe6, 0xd8, 0xe7, 0xb1, 0xe3, 0x73, 0x49, 0x1d, 0x22,
0x84, 0x23, 0x24, 0xff, 0x3e, 0x8f, 0x09, 0x73, 0x7c, 0x1e, 0xc7, 0x84, 0x05, 0xe6, 0x13, 0x0b,
0xc9, 0x53, 0x8e, 0x0e, 0x0d, 0x45, 0x52, 0x4c, 0x84, 0xc0, 0x06, 0x8e, 0x35, 0xcc, 0x3a, 0xa9,
0x68, 0x66, 0xe7, 0x9c, 0x39, 0x39, 0xdb, 0xe7, 0x91, 0x33, 0x4d, 0xa8, 0x54, 0x5a, 0xd6, 0xb3,
0xd5, 0xd0, 0x84, 0xca, 0x90, 0x44, 0x4e, 0x3a, 0x17, 0x34, 0xf8, 0x1c, 0xd3, 0x24, 0x21, 0x13,
0xaa, 0x19, 0x0f, 0x97, 0x18, 0xec, 0x4b, 0x38, 0x51, 0x3f, 0xda, 0x17, 0xb0, 0x37, 0x0c, 0x82,
0x0f, 0x09, 0x95, 0x63, 0x41, 0x25, 0x49, 0x43, 0xce, 0xd0, 0x00, 0xee, 0x64, 0x86, 0xfd, 0xd6,
0x51, 0xeb, 0x78, 0xc7, 0x3d, 0xc2, 0x85, 0xf4, 0xca, 0x0d, 0x9b, 0x60, 0x38, 0x23, 0x7a, 0x39,
0xda, 0x7e, 0x0a, 0x1d, 0x8f, 0xc6, 0x7c, 0x46, 0xcb, 0x62, 0x5d, 0xb8, 0x4b, 0x63, 0x12, 0x46,
0xb9, 0xda, 0xb6, 0xa7, 0xbe, 0xd8, 0x63, 0x78, 0x30, 0x0c, 0x82, 0xb7, 0xec, 0x9a, 0x4f, 0x59,
0xe0, 0xd1, 0xaf, 0x53, 0x9a, 0xa4, 0xe8, 0x25, 0x6c, 0x86, 0xea, 0x64, 0x95, 0xb5, 0x06, 0x5f,
0x10, 0x16, 0x44, 0x54, 0x8e, 0xf2, 0x21, 0x3c, 0x43, 0xb0, 0xbb, 0x80, 0x8a, 0x82, 0x89, 0xe0,
0x2c, 0xa1, 0xf6, 0x31, 0x74, 0x55, 0xa6, 0x8a, 0xd3, 0x1e, 0xdc, 0x4e, 0xc9, 0x44, 0x47, 0xca,
0x1e, 0xed, 0x03, 0xe8, 0x55, 0x90, 0x5a, 0x22, 0x86, 0xce, 0x30, 0x4a, 0xa9, 0xac, 0x53, 0x40,
0x6f, 0x60, 0x9b, 0x9b, 0xa9, 0xfb, 0x1b, 0x79, 0xfe, 0x27, 0x2b, 0x56, 0xa7, 0x2e, 0x0a, 0xbf,
0xcf, 0x2e, 0xea, 0x9d, 0xba, 0x27, 0xef, 0x86, 0x68, 0xef, 0x43, 0xb7, 0x6c, 0xa7, 0x63, 0x5c,
0xe5, 0xf3, 0x8d, 0xa7, 0x69, 0x29, 0xc5, 0x2b, 0xd8, 0xe2, 0xfa, 0x48, 0xaf, 0xec, 0x51, 0xd1,
0xd2, 0xc0, 0xcb, 0x3b, 0x5b, 0x50, 0xec, 0x1e, 0x74, 0x4a, 0xa2, 0xda, 0xeb, 0xc4, 0xec, 0xa2,
0x6a, 0xb7, 0xbc, 0xb6, 0x3e, 0xec, 0x57, 0xa1, 0x5a, 0x84, 0xe9, 0x41, 0x6a, 0x35, 0xfe, 0xd3,
0xe2, 0x0e, 0xa0, 0x57, 0xf1, 0xd3, 0x41, 0xb6, 0xa0, 0xad, 0x06, 0x77, 0xff, 0xb4, 0x61, 0x57,
0xaf, 0xe2, 0x8a, 0xca, 0x59, 0xe8, 0x53, 0xf4, 0x0d, 0xe0, 0xa6, 0x36, 0xc8, 0xc5, 0x35, 0x2f,
0x2a, 0x5e, 0x2a, 0xad, 0x75, 0xda, 0x88, 0xa3, 0x33, 0xdd, 0x42, 0xbf, 0x5a, 0x70, 0xbf, 0x54,
0x38, 0x74, 0x56, 0x2b, 0xb4, 0xaa, 0xca, 0xd6, 0x79, 0x53, 0xda, 0x22, 0xc2, 0x4f, 0xb8, 0x57,
0xac, 0x1a, 0x1a, 0xd4, 0x4f, 0xb2, 0xfc, 0x22, 0x58, 0x67, 0x0d, 0x59, 0x0b, 0xfb, 0x1f, 0xb0,
0x53, 0x28, 0x1f, 0x5a, 0x6b, 0x8f, 0x95, 0x32, 0x59, 0x83, 0x66, 0xa4, 0x85, 0xf7, 0xef, 0x16,
0xec, 0x96, 0x7b, 0x8b, 0xd6, 0xdd, 0x63, 0x35, 0xc2, 0x8b, 0xc6, 0xbc, 0x52, 0x07, 0x4a, 0x9d,
0x45, 0x6b, 0x2e, 0xb3, 0x9a, 0xe1, 0xbc, 0x29, 0xcd, 0x44, 0x78, 0xed, 0xc1, 0x63, 0x9f, 0xc7,
0x75, 0xf4, 0xcb, 0xd6, 0xa7, 0x4d, 0xfd, 0xf8, 0x77, 0xe3, 0xf0, 0xa3, 0xeb, 0x91, 0x39, 0x1e,
0x65, 0xe0, 0xa1, 0x10, 0xf8, 0xd2, 0x80, 0x47, 0x0a, 0x71, 0xdd, 0xce, 0xff, 0x1d, 0x4e, 0xff,
0x05, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x05, 0xaa, 0x44, 0x29, 0x07, 0x00, 0x00,
}

View File

@ -0,0 +1,80 @@
syntax = "proto3";
package v2ray.core.app.proxyman.command;
option csharp_namespace = "V2Ray.Core.App.Proxyman.Command";
option go_package = "command";
option java_package = "com.v2ray.core.app.proxyman.command";
option java_multiple_files = true;
import "v2ray.com/core/common/protocol/user.proto";
import "v2ray.com/core/common/serial/typed_message.proto";
import "v2ray.com/core/config.proto";
message AddUserOperation {
v2ray.core.common.protocol.User user = 1;
}
message RemoveUserOperation {
string email = 1;
}
message AddInboundRequest {
core.InboundHandlerConfig inbound = 1;
}
message AddInboundResponse{
}
message RemoveInboundRequest {
string tag = 1;
}
message RemoveInboundResponse {}
message AlterInboundRequest {
string tag = 1;
v2ray.core.common.serial.TypedMessage operation = 2;
}
message AlterInboundResponse {
}
message AddOutboundRequest {
core.OutboundHandlerConfig outbound = 1;
}
message AddOutboundResponse {
}
message RemoveOutboundRequest {
string tag = 1;
}
message RemoveOutboundResponse {
}
message AlterOutboundRequest {
string tag = 1;
v2ray.core.common.serial.TypedMessage operation = 2;
}
message AlterOutboundResponse {
}
service HandlerService {
rpc AddInbound(AddInboundRequest) returns (AddInboundResponse) {}
rpc RemoveInbound(RemoveInboundRequest) returns (RemoveInboundResponse) {}
rpc AlterInbound(AlterInboundRequest) returns (AlterInboundResponse) {}
rpc AddOutbound(AddOutboundRequest) returns (AddOutboundResponse) {}
rpc RemoveOutbound(RemoveOutboundRequest) returns (RemoveOutboundResponse) {}
rpc AlterOutbound(AlterOutboundRequest) returns (AlterOutboundResponse) {}
}
message Config {}

View File

@ -0,0 +1,3 @@
package command
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Proxyman,Command

View File

@ -0,0 +1,7 @@
package command
import "v2ray.com/core/common/errors"
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).Path("App", "Proxyman", "Command")
}

View File

@ -1,11 +1,5 @@
package proxyman
import (
"context"
"v2ray.com/core/proxy"
)
func (s *AllocationStrategy) GetConcurrencyValue() uint32 {
if s == nil || s.Concurrency == nil {
return 3
@ -19,14 +13,3 @@ func (s *AllocationStrategy) GetRefreshValue() uint32 {
}
return s.Refresh.Value
}
func (c *OutboundHandlerConfig) GetProxyHandler(ctx context.Context) (proxy.Outbound, error) {
if c == nil {
return nil, newError("OutboundHandlerConfig is nil")
}
config, err := c.ProxySettings.GetInstance()
if err != nil {
return nil, err
}
return proxy.CreateOutboundHandler(ctx, config)
}

View File

@ -3,7 +3,6 @@ package proxyman
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import v2ray_core_common_serial "v2ray.com/core/common/serial"
import v2ray_core_common_net "v2ray.com/core/common/net"
import v2ray_core_common_net1 "v2ray.com/core/common/net"
import v2ray_core_transport_internet "v2ray.com/core/transport/internet"
@ -211,45 +210,13 @@ func (m *ReceiverConfig) GetDomainOverride() []KnownProtocols {
return nil
}
type InboundHandlerConfig struct {
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
ReceiverSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings" json:"receiver_settings,omitempty"`
ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"`
}
func (m *InboundHandlerConfig) Reset() { *m = InboundHandlerConfig{} }
func (m *InboundHandlerConfig) String() string { return proto.CompactTextString(m) }
func (*InboundHandlerConfig) ProtoMessage() {}
func (*InboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *InboundHandlerConfig) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *InboundHandlerConfig) GetReceiverSettings() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.ReceiverSettings
}
return nil
}
func (m *InboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.ProxySettings
}
return nil
}
type OutboundConfig struct {
}
func (m *OutboundConfig) Reset() { *m = OutboundConfig{} }
func (m *OutboundConfig) String() string { return proto.CompactTextString(m) }
func (*OutboundConfig) ProtoMessage() {}
func (*OutboundConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (*OutboundConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
type SenderConfig struct {
// Send traffic through the given IP. Only IP is allowed.
@ -262,7 +229,7 @@ type SenderConfig struct {
func (m *SenderConfig) Reset() { *m = SenderConfig{} }
func (m *SenderConfig) String() string { return proto.CompactTextString(m) }
func (*SenderConfig) ProtoMessage() {}
func (*SenderConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (*SenderConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *SenderConfig) GetVia() *v2ray_core_common_net.IPOrDomain {
if m != nil {
@ -292,54 +259,6 @@ func (m *SenderConfig) GetMultiplexSettings() *MultiplexingConfig {
return nil
}
type OutboundHandlerConfig struct {
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
SenderSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=sender_settings,json=senderSettings" json:"sender_settings,omitempty"`
ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"`
Expire int64 `protobuf:"varint,4,opt,name=expire" json:"expire,omitempty"`
Comment string `protobuf:"bytes,5,opt,name=comment" json:"comment,omitempty"`
}
func (m *OutboundHandlerConfig) Reset() { *m = OutboundHandlerConfig{} }
func (m *OutboundHandlerConfig) String() string { return proto.CompactTextString(m) }
func (*OutboundHandlerConfig) ProtoMessage() {}
func (*OutboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *OutboundHandlerConfig) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *OutboundHandlerConfig) GetSenderSettings() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.SenderSettings
}
return nil
}
func (m *OutboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.ProxySettings
}
return nil
}
func (m *OutboundHandlerConfig) GetExpire() int64 {
if m != nil {
return m.Expire
}
return 0
}
func (m *OutboundHandlerConfig) GetComment() string {
if m != nil {
return m.Comment
}
return ""
}
type MultiplexingConfig struct {
// Whether or not Mux is enabled.
Enabled bool `protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"`
@ -350,7 +269,7 @@ type MultiplexingConfig struct {
func (m *MultiplexingConfig) Reset() { *m = MultiplexingConfig{} }
func (m *MultiplexingConfig) String() string { return proto.CompactTextString(m) }
func (*MultiplexingConfig) ProtoMessage() {}
func (*MultiplexingConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
func (*MultiplexingConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *MultiplexingConfig) GetEnabled() bool {
if m != nil {
@ -372,10 +291,8 @@ func init() {
proto.RegisterType((*AllocationStrategy_AllocationStrategyConcurrency)(nil), "v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency")
proto.RegisterType((*AllocationStrategy_AllocationStrategyRefresh)(nil), "v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefresh")
proto.RegisterType((*ReceiverConfig)(nil), "v2ray.core.app.proxyman.ReceiverConfig")
proto.RegisterType((*InboundHandlerConfig)(nil), "v2ray.core.app.proxyman.InboundHandlerConfig")
proto.RegisterType((*OutboundConfig)(nil), "v2ray.core.app.proxyman.OutboundConfig")
proto.RegisterType((*SenderConfig)(nil), "v2ray.core.app.proxyman.SenderConfig")
proto.RegisterType((*OutboundHandlerConfig)(nil), "v2ray.core.app.proxyman.OutboundHandlerConfig")
proto.RegisterType((*MultiplexingConfig)(nil), "v2ray.core.app.proxyman.MultiplexingConfig")
proto.RegisterEnum("v2ray.core.app.proxyman.KnownProtocols", KnownProtocols_name, KnownProtocols_value)
proto.RegisterEnum("v2ray.core.app.proxyman.AllocationStrategy_Type", AllocationStrategy_Type_name, AllocationStrategy_Type_value)
@ -384,57 +301,49 @@ func init() {
func init() { proto.RegisterFile("v2ray.com/core/app/proxyman/config.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 822 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xd1, 0x8e, 0xdb, 0x44,
0x14, 0xad, 0xe3, 0x34, 0xc9, 0xde, 0xed, 0x7a, 0xdd, 0xa1, 0xd0, 0x10, 0x40, 0x0a, 0x01, 0xd1,
0xa8, 0x20, 0xa7, 0xa4, 0xe2, 0x81, 0x27, 0x58, 0x76, 0x2b, 0x75, 0x81, 0x55, 0xcc, 0x24, 0xe2,
0xa1, 0x42, 0xb2, 0x66, 0xed, 0xa9, 0x19, 0x61, 0xcf, 0x58, 0x33, 0x93, 0x74, 0xfd, 0x4b, 0x7c,
0x05, 0x8f, 0x3c, 0xf0, 0x05, 0xfc, 0x0a, 0x2f, 0xc8, 0x9e, 0x71, 0x76, 0xb7, 0x49, 0x5a, 0x96,
0xaa, 0x6f, 0x33, 0xc9, 0x39, 0xc7, 0x73, 0xcf, 0x3d, 0x77, 0x06, 0xc6, 0xab, 0xa9, 0x24, 0x65,
0x10, 0x8b, 0x7c, 0x12, 0x0b, 0x49, 0x27, 0xa4, 0x28, 0x26, 0x85, 0x14, 0x17, 0x65, 0x4e, 0xf8,
0x24, 0x16, 0xfc, 0x39, 0x4b, 0x83, 0x42, 0x0a, 0x2d, 0xd0, 0xfd, 0x06, 0x29, 0x69, 0x40, 0x8a,
0x22, 0x68, 0x50, 0x83, 0x47, 0x2f, 0x49, 0xc4, 0x22, 0xcf, 0x05, 0x9f, 0x28, 0x2a, 0x19, 0xc9,
0x26, 0xba, 0x2c, 0x68, 0x12, 0xe5, 0x54, 0x29, 0x92, 0x52, 0x23, 0x35, 0x78, 0xb0, 0x9d, 0xc1,
0xa9, 0x9e, 0x90, 0x24, 0x91, 0x54, 0x29, 0x0b, 0xfc, 0x74, 0x37, 0xb0, 0x10, 0x52, 0x5b, 0x54,
0xf0, 0x12, 0x4a, 0x4b, 0xc2, 0x55, 0xf5, 0xff, 0x84, 0x71, 0x4d, 0x65, 0x85, 0xbe, 0x5a, 0xc9,
0xe8, 0x10, 0x0e, 0x4e, 0xf9, 0xb9, 0x58, 0xf2, 0xe4, 0xb8, 0xfe, 0x79, 0xf4, 0x87, 0x0b, 0xe8,
0x28, 0xcb, 0x44, 0x4c, 0x34, 0x13, 0x7c, 0xae, 0x25, 0xd1, 0x34, 0x2d, 0xd1, 0x09, 0xb4, 0xab,
0xd3, 0xf7, 0x9d, 0xa1, 0x33, 0xf6, 0xa6, 0x8f, 0x82, 0x1d, 0x06, 0x04, 0x9b, 0xd4, 0x60, 0x51,
0x16, 0x14, 0xd7, 0x6c, 0xf4, 0x1b, 0xec, 0xc7, 0x82, 0xc7, 0x4b, 0x29, 0x29, 0x8f, 0xcb, 0x7e,
0x6b, 0xe8, 0x8c, 0xf7, 0xa7, 0xa7, 0x37, 0x11, 0xdb, 0xfc, 0xe9, 0xf8, 0x52, 0x10, 0x5f, 0x55,
0x47, 0x11, 0x74, 0x25, 0x7d, 0x2e, 0xa9, 0xfa, 0xb5, 0xef, 0xd6, 0x1f, 0x7a, 0xf2, 0x66, 0x1f,
0xc2, 0x46, 0x0c, 0x37, 0xaa, 0x83, 0xaf, 0xe0, 0xa3, 0x57, 0x1e, 0x07, 0xdd, 0x83, 0xdb, 0x2b,
0x92, 0x2d, 0x8d, 0x6b, 0x07, 0xd8, 0x6c, 0x06, 0x5f, 0xc2, 0xfb, 0x3b, 0xc5, 0xb7, 0x53, 0x46,
0x5f, 0x40, 0xbb, 0x72, 0x11, 0x01, 0x74, 0x8e, 0xb2, 0x17, 0xa4, 0x54, 0xfe, 0xad, 0x6a, 0x8d,
0x09, 0x4f, 0x44, 0xee, 0x3b, 0xe8, 0x0e, 0xf4, 0x9e, 0x5c, 0x54, 0xed, 0x25, 0x99, 0xdf, 0x1a,
0xfd, 0xed, 0x82, 0x87, 0x69, 0x4c, 0xd9, 0x8a, 0x4a, 0xd3, 0x55, 0xf4, 0x0d, 0x40, 0x15, 0x82,
0x48, 0x12, 0x9e, 0x1a, 0xed, 0xfd, 0xe9, 0xf0, 0xaa, 0x1d, 0x26, 0x4d, 0x01, 0xa7, 0x3a, 0x08,
0x85, 0xd4, 0xb8, 0xc2, 0xe1, 0xbd, 0xa2, 0x59, 0xa2, 0xaf, 0xa1, 0x93, 0x31, 0xa5, 0x29, 0xb7,
0x4d, 0xfb, 0x78, 0x07, 0xf9, 0x34, 0x9c, 0xc9, 0x13, 0x91, 0x13, 0xc6, 0xb1, 0x25, 0xa0, 0x5f,
0xe0, 0x1d, 0xb2, 0xae, 0x37, 0x52, 0xb6, 0x60, 0xdb, 0x93, 0xcf, 0x6f, 0xd0, 0x13, 0x8c, 0xc8,
0x66, 0x30, 0x17, 0x70, 0xa8, 0xb4, 0xa4, 0x24, 0x8f, 0x14, 0xd5, 0x9a, 0xf1, 0x54, 0xf5, 0xdb,
0x9b, 0xca, 0xeb, 0x31, 0x08, 0x9a, 0x31, 0x08, 0xe6, 0x35, 0xcb, 0xf8, 0x83, 0x3d, 0xa3, 0x31,
0xb7, 0x12, 0xe8, 0x5b, 0xf8, 0x50, 0x1a, 0x07, 0x23, 0x21, 0x59, 0xca, 0x38, 0xc9, 0xa2, 0x84,
0x2a, 0xcd, 0x78, 0xfd, 0xf5, 0xfe, 0xed, 0xa1, 0x33, 0xee, 0xe1, 0x81, 0xc5, 0xcc, 0x2c, 0xe4,
0xe4, 0x12, 0x81, 0x42, 0x38, 0x4c, 0x6a, 0x1f, 0x22, 0xb1, 0xa2, 0x52, 0xb2, 0x84, 0xf6, 0xbb,
0x43, 0x77, 0xec, 0x4d, 0x1f, 0xec, 0xac, 0xf8, 0x07, 0x2e, 0x5e, 0xf0, 0xb0, 0x1a, 0xcb, 0x58,
0x64, 0x0a, 0x7b, 0x86, 0x3f, 0xb3, 0xf4, 0xef, 0xdb, 0xbd, 0x8e, 0xdf, 0x1d, 0xfd, 0xe5, 0xc0,
0x3d, 0x3b, 0xb1, 0x4f, 0x09, 0x4f, 0xb2, 0x75, 0x8b, 0x7d, 0x70, 0x35, 0x49, 0xeb, 0xde, 0xee,
0xe1, 0x6a, 0x89, 0xe6, 0x70, 0xd7, 0x1e, 0x50, 0x5e, 0x9a, 0x63, 0xda, 0xf7, 0xd9, 0x96, 0xf6,
0x99, 0x4b, 0xaa, 0x1e, 0xd7, 0xe4, 0xcc, 0xdc, 0x51, 0xd8, 0x6f, 0x04, 0xd6, 0xce, 0x9c, 0x81,
0x57, 0x1f, 0xf8, 0x52, 0xd1, 0xbd, 0x91, 0xe2, 0x41, 0xcd, 0x6e, 0xe4, 0x46, 0x3e, 0x78, 0xb3,
0xa5, 0xbe, 0x7a, 0x01, 0xfd, 0xd9, 0x82, 0x3b, 0x73, 0xca, 0x93, 0x75, 0x61, 0x8f, 0xc1, 0x5d,
0x31, 0x62, 0x43, 0xfb, 0x1f, 0x72, 0x57, 0xa1, 0xb7, 0xc5, 0xa2, 0xf5, 0xe6, 0xb1, 0xf8, 0x69,
0x47, 0xf1, 0x0f, 0x5f, 0x23, 0x1a, 0x56, 0x24, 0xab, 0x79, 0xdd, 0x00, 0xf4, 0x0c, 0x50, 0xbe,
0xcc, 0x34, 0x2b, 0x32, 0x7a, 0xf1, 0xca, 0x08, 0x5f, 0x8b, 0xca, 0x59, 0x43, 0x61, 0x3c, 0xb5,
0xba, 0x77, 0xd7, 0x32, 0x6b, 0x73, 0xff, 0x71, 0xe0, 0xdd, 0xc6, 0xdd, 0xd7, 0x85, 0x65, 0x06,
0x87, 0xaa, 0x76, 0xfd, 0xff, 0x46, 0xc5, 0x33, 0xf4, 0xb7, 0x14, 0x14, 0xf4, 0x1e, 0x74, 0xe8,
0x45, 0xc1, 0x24, 0xad, 0xbd, 0x71, 0xb1, 0xdd, 0xa1, 0x3e, 0x74, 0x2b, 0x11, 0xca, 0x75, 0x3d,
0x94, 0x7b, 0xb8, 0xd9, 0x8e, 0x42, 0x40, 0x9b, 0x36, 0x55, 0x78, 0xca, 0xc9, 0x79, 0x46, 0x93,
0xba, 0xfa, 0x1e, 0x6e, 0xb6, 0x68, 0xb8, 0xf9, 0x38, 0x1d, 0x5c, 0x7b, 0x51, 0x1e, 0x7e, 0x02,
0xde, 0xf5, 0x19, 0x45, 0x3d, 0x68, 0x3f, 0x5d, 0x2c, 0x42, 0xff, 0x16, 0xea, 0x82, 0xbb, 0xf8,
0x71, 0xee, 0x3b, 0xdf, 0x1d, 0xc3, 0x07, 0xb1, 0xc8, 0x77, 0x75, 0x2e, 0x74, 0x9e, 0xf5, 0x9a,
0xf5, 0xef, 0xad, 0xfb, 0x3f, 0x4f, 0x31, 0x29, 0x83, 0xe3, 0x0a, 0x75, 0x54, 0x14, 0x26, 0x27,
0x39, 0xe1, 0xe7, 0x9d, 0xfa, 0x75, 0x7e, 0xfc, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x25, 0xdf,
0x6a, 0xb2, 0x93, 0x08, 0x00, 0x00,
// 691 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0x5d, 0x6f, 0xd3, 0x3a,
0x1c, 0xc6, 0x97, 0xb6, 0x6b, 0x7b, 0xfe, 0x5b, 0xb3, 0x1c, 0x9f, 0x23, 0x2d, 0xa7, 0x07, 0xa4,
0x52, 0x90, 0x56, 0x0d, 0x94, 0x40, 0x27, 0x2e, 0xb8, 0x82, 0xd1, 0x4d, 0x62, 0xbc, 0xa8, 0xc1,
0xad, 0xb8, 0x98, 0x90, 0x22, 0x2f, 0xf1, 0x8a, 0x45, 0x62, 0x47, 0x8e, 0xdb, 0x2d, 0x5f, 0x89,
0x4f, 0xc1, 0x25, 0x9f, 0x81, 0x4f, 0x83, 0xf2, 0xd6, 0x75, 0xeb, 0x3a, 0x98, 0x76, 0xe7, 0xa6,
0xcf, 0xf3, 0xb3, 0xfd, 0x3c, 0xb6, 0xa1, 0x37, 0xeb, 0x4b, 0x92, 0x58, 0x9e, 0x08, 0x6d, 0x4f,
0x48, 0x6a, 0x93, 0x28, 0xb2, 0x23, 0x29, 0xce, 0x93, 0x90, 0x70, 0xdb, 0x13, 0xfc, 0x94, 0x4d,
0xac, 0x48, 0x0a, 0x25, 0xd0, 0x76, 0xa9, 0x94, 0xd4, 0x22, 0x51, 0x64, 0x95, 0xaa, 0xf6, 0xce,
0x15, 0x84, 0x27, 0xc2, 0x50, 0x70, 0x9b, 0x53, 0x65, 0x13, 0xdf, 0x97, 0x34, 0x8e, 0x73, 0x42,
0xfb, 0xd1, 0x6a, 0x61, 0x24, 0xa4, 0x2a, 0x54, 0xd6, 0x15, 0x95, 0x92, 0x84, 0xc7, 0xe9, 0xff,
0x36, 0xe3, 0x8a, 0xca, 0x54, 0xbd, 0xb8, 0xae, 0xee, 0x16, 0xb4, 0x8e, 0xf8, 0x89, 0x98, 0x72,
0x7f, 0x90, 0x7d, 0xee, 0x7e, 0xaf, 0x02, 0xda, 0x0f, 0x02, 0xe1, 0x11, 0xc5, 0x04, 0x1f, 0x29,
0x49, 0x14, 0x9d, 0x24, 0xe8, 0x00, 0x6a, 0x2a, 0x89, 0xa8, 0xa9, 0x75, 0xb4, 0x9e, 0xde, 0x7f,
0x6a, 0xad, 0xd8, 0x8e, 0xb5, 0x6c, 0xb5, 0xc6, 0x49, 0x44, 0x71, 0xe6, 0x46, 0x5f, 0x61, 0xc3,
0x13, 0xdc, 0x9b, 0x4a, 0x49, 0xb9, 0x97, 0x98, 0x95, 0x8e, 0xd6, 0xdb, 0xe8, 0x1f, 0xdd, 0x06,
0xb6, 0xfc, 0x69, 0x70, 0x01, 0xc4, 0x8b, 0x74, 0xe4, 0x42, 0x43, 0xd2, 0x53, 0x49, 0xe3, 0x2f,
0x66, 0x35, 0x9b, 0xe8, 0xf0, 0x6e, 0x13, 0xe1, 0x1c, 0x86, 0x4b, 0x6a, 0xfb, 0x39, 0xdc, 0xbf,
0x71, 0x39, 0xe8, 0x5f, 0x58, 0x9f, 0x91, 0x60, 0x9a, 0xa7, 0xd6, 0xc2, 0xf9, 0x8f, 0xf6, 0x33,
0xf8, 0x6f, 0x25, 0xfc, 0x7a, 0x4b, 0xf7, 0x09, 0xd4, 0xd2, 0x14, 0x11, 0x40, 0x7d, 0x3f, 0x38,
0x23, 0x49, 0x6c, 0xac, 0xa5, 0x63, 0x4c, 0xb8, 0x2f, 0x42, 0x43, 0x43, 0x9b, 0xd0, 0x3c, 0x3c,
0x4f, 0xeb, 0x25, 0x81, 0x51, 0xe9, 0xfe, 0xac, 0x82, 0x8e, 0xa9, 0x47, 0xd9, 0x8c, 0xca, 0xbc,
0x55, 0xf4, 0x12, 0x20, 0x3d, 0x04, 0xae, 0x24, 0x7c, 0x92, 0xb3, 0x37, 0xfa, 0x9d, 0xc5, 0x38,
0xf2, 0xd3, 0x64, 0x71, 0xaa, 0x2c, 0x47, 0x48, 0x85, 0x53, 0x1d, 0xfe, 0x2b, 0x2a, 0x87, 0xe8,
0x05, 0xd4, 0x03, 0x16, 0x2b, 0xca, 0x8b, 0xd2, 0x1e, 0xac, 0x30, 0x1f, 0x39, 0x43, 0x79, 0x20,
0x42, 0xc2, 0x38, 0x2e, 0x0c, 0xe8, 0x33, 0xfc, 0x43, 0xe6, 0xfb, 0x75, 0xe3, 0x62, 0xc3, 0x45,
0x27, 0x8f, 0x6f, 0xd1, 0x09, 0x46, 0x64, 0xf9, 0x60, 0x8e, 0x61, 0x2b, 0x56, 0x92, 0x92, 0xd0,
0x8d, 0xa9, 0x52, 0x8c, 0x4f, 0x62, 0xb3, 0xb6, 0x4c, 0x9e, 0x5f, 0x03, 0xab, 0xbc, 0x06, 0xd6,
0x28, 0x73, 0xe5, 0xf9, 0x60, 0x3d, 0x67, 0x8c, 0x0a, 0x04, 0x7a, 0x05, 0xf7, 0x64, 0x9e, 0xa0,
0x2b, 0x24, 0x9b, 0x30, 0x4e, 0x02, 0xd7, 0xa7, 0xb1, 0x62, 0x3c, 0x9b, 0xdd, 0x5c, 0xef, 0x68,
0xbd, 0x26, 0x6e, 0x17, 0x9a, 0x61, 0x21, 0x39, 0xb8, 0x50, 0x20, 0x07, 0xb6, 0xfc, 0x2c, 0x07,
0x57, 0xcc, 0xa8, 0x94, 0xcc, 0xa7, 0x66, 0xa3, 0x53, 0xed, 0xe9, 0xfd, 0x9d, 0x95, 0x3b, 0x7e,
0xc7, 0xc5, 0x19, 0x77, 0xd2, 0x6b, 0xe9, 0x89, 0x20, 0xc6, 0x7a, 0xee, 0x1f, 0x16, 0xf6, 0xb7,
0xb5, 0x66, 0xdd, 0x68, 0x74, 0x0d, 0xd0, 0x87, 0x53, 0xb5, 0x78, 0x63, 0x7f, 0x54, 0x60, 0x73,
0x44, 0xb9, 0x3f, 0x2f, 0x7b, 0x0f, 0xaa, 0x33, 0x46, 0x8a, 0x96, 0xff, 0xa0, 0xa8, 0x54, 0x7d,
0x5d, 0x8e, 0x95, 0xbb, 0xe7, 0xf8, 0x11, 0xf4, 0x6c, 0x7b, 0x17, 0xd0, 0xbc, 0xf6, 0xdd, 0xdf,
0x40, 0x9d, 0xd4, 0x54, 0x30, 0x5b, 0x19, 0x61, 0x8e, 0x3c, 0x06, 0x14, 0x4e, 0x03, 0xc5, 0xa2,
0x80, 0x9e, 0xdf, 0xd8, 0xf9, 0xa5, 0x6c, 0x3f, 0x94, 0x16, 0xc6, 0x27, 0x05, 0xf7, 0xef, 0x39,
0xa6, 0x64, 0x77, 0x1d, 0x40, 0xcb, 0x42, 0x64, 0x42, 0x83, 0x72, 0x72, 0x12, 0x50, 0x3f, 0xcb,
0xb4, 0x89, 0xcb, 0x9f, 0xa8, 0xb3, 0xfc, 0x9e, 0xb5, 0x2e, 0x3d, 0x42, 0xbb, 0x0f, 0x41, 0xbf,
0x5c, 0x2b, 0x6a, 0x42, 0xed, 0xcd, 0x78, 0xec, 0x18, 0x6b, 0xa8, 0x01, 0xd5, 0xf1, 0xfb, 0x91,
0xa1, 0xbd, 0x1e, 0xc0, 0xff, 0x9e, 0x08, 0x57, 0xad, 0xdd, 0xd1, 0x8e, 0x9b, 0xe5, 0xf8, 0x5b,
0x65, 0xfb, 0x53, 0x1f, 0x93, 0xc4, 0x1a, 0xa4, 0xaa, 0xfd, 0x28, 0xca, 0x93, 0x0a, 0x09, 0x3f,
0xa9, 0x67, 0x0f, 0xfa, 0xde, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x77, 0x7f, 0xdc, 0x8e, 0x94,
0x06, 0x00, 0x00,
}

View File

@ -6,7 +6,6 @@ option go_package = "proxyman";
option java_package = "com.v2ray.core.app.proxyman";
option java_multiple_files = true;
import "v2ray.com/core/common/serial/typed_message.proto";
import "v2ray.com/core/common/net/address.proto";
import "v2ray.com/core/common/net/port.proto";
import "v2ray.com/core/transport/internet/config.proto";
@ -61,15 +60,6 @@ message ReceiverConfig {
repeated KnownProtocols domain_override = 7;
}
message InboundHandlerConfig {
// Tag of the inbound handler.
string tag = 1;
// Settings for how this inbound proxy is handled. Must be ReceiverConfig above.
v2ray.core.common.serial.TypedMessage receiver_settings = 2;
// Settings for inbound proxy. Must be one of the inbound proxies.
v2ray.core.common.serial.TypedMessage proxy_settings = 3;
}
message OutboundConfig {
}
@ -82,19 +72,6 @@ message SenderConfig {
MultiplexingConfig multiplex_settings = 4;
}
message OutboundHandlerConfig {
// Tag of this outbound handler.
string tag = 1;
// Settings for how to dial connection for this outbound handler. Must be SenderConfig above.
v2ray.core.common.serial.TypedMessage sender_settings = 2;
// Settings for this outbound proxy. Must be one of the outbound proxies.
v2ray.core.common.serial.TypedMessage proxy_settings = 3;
// If not zero, this outbound will be expired in seconds. Not used for now.
int64 expire = 4;
// Comment of this outbound handler. Not used for now.
string comment = 5;
}
message MultiplexingConfig {
// Whether or not Mux is enabled.
bool enabled = 1;

View File

@ -5,6 +5,7 @@ import (
"v2ray.com/core/app/proxyman"
"v2ray.com/core/app/proxyman/mux"
"v2ray.com/core/common"
"v2ray.com/core/common/dice"
"v2ray.com/core/common/net"
"v2ray.com/core/proxy"
@ -14,17 +15,23 @@ type AlwaysOnInboundHandler struct {
proxy proxy.Inbound
workers []worker
mux *mux.Server
tag string
}
func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) {
p, err := proxy.CreateInboundHandler(ctx, proxyConfig)
rawProxy, err := common.CreateObject(ctx, proxyConfig)
if err != nil {
return nil, err
}
p, ok := rawProxy.(proxy.Inbound)
if !ok {
return nil, newError("not an inbound proxy.")
}
h := &AlwaysOnInboundHandler{
proxy: p,
mux: mux.NewServer(ctx),
tag: tag,
}
nl := p.Network()
@ -74,16 +81,26 @@ func (h *AlwaysOnInboundHandler) Start() error {
return nil
}
func (h *AlwaysOnInboundHandler) Close() {
func (h *AlwaysOnInboundHandler) Close() error {
for _, worker := range h.workers {
worker.Close()
}
h.mux.Close()
return nil
}
func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (proxy.Inbound, net.Port, int) {
func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) {
if len(h.workers) == 0 {
return nil, 0, 0
}
w := h.workers[dice.Roll(len(h.workers))]
return w.Proxy(), w.Port(), 9999
}
func (h *AlwaysOnInboundHandler) Tag() string {
return h.tag
}
func (h *AlwaysOnInboundHandler) GetInbound() proxy.Inbound {
return h.proxy
}

View File

@ -5,17 +5,18 @@ import (
"sync"
"time"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/app/proxyman/mux"
"v2ray.com/core/common/dice"
"v2ray.com/core/common/net"
"v2ray.com/core/common/signal"
"v2ray.com/core/proxy"
)
type DynamicInboundHandler struct {
tag string
ctx context.Context
cancel context.CancelFunc
v *core.Instance
proxyConfig interface{}
receiverConfig *proxyman.ReceiverConfig
portMutex sync.Mutex
@ -24,18 +25,26 @@ type DynamicInboundHandler struct {
worker []worker
lastRefresh time.Time
mux *mux.Server
task *signal.PeriodicTask
}
func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) {
ctx, cancel := context.WithCancel(ctx)
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
h := &DynamicInboundHandler{
ctx: ctx,
tag: tag,
cancel: cancel,
proxyConfig: proxyConfig,
receiverConfig: receiverConfig,
portsInUse: make(map[net.Port]bool),
mux: mux.NewServer(ctx),
v: v,
}
h.task = &signal.PeriodicTask{
Interval: time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()),
Execute: h.refresh,
}
return h, nil
@ -59,9 +68,7 @@ func (h *DynamicInboundHandler) allocatePort() net.Port {
}
}
func (h *DynamicInboundHandler) waitAnyCloseWorkers(ctx context.Context, cancel context.CancelFunc, workers []worker, duration time.Duration) {
time.Sleep(duration)
cancel()
func (h *DynamicInboundHandler) closeWorkers(workers []worker) {
ports2Del := make([]net.Port, len(workers))
for idx, worker := range workers {
ports2Del[idx] = worker.Port()
@ -80,7 +87,6 @@ func (h *DynamicInboundHandler) refresh() error {
timeout := time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()) * 2
concurrency := h.receiverConfig.AllocationStrategy.GetConcurrencyValue()
ctx, cancel := context.WithTimeout(h.ctx, timeout)
workers := make([]worker, 0, concurrency)
address := h.receiverConfig.Listen.AsAddress()
@ -89,11 +95,12 @@ func (h *DynamicInboundHandler) refresh() error {
}
for i := uint32(0); i < concurrency; i++ {
port := h.allocatePort()
p, err := proxy.CreateInboundHandler(ctx, h.proxyConfig)
rawProxy, err := h.v.CreateObject(h.proxyConfig)
if err != nil {
newError("failed to create proxy instance").Base(err).AtWarning().WriteToLog()
continue
}
p := rawProxy.(proxy.Inbound)
nl := p.Network()
if nl.HasNetwork(net.Network_TCP) {
worker := &tcpWorker{
@ -134,36 +141,22 @@ func (h *DynamicInboundHandler) refresh() error {
h.worker = workers
h.workerMutex.Unlock()
go h.waitAnyCloseWorkers(ctx, cancel, workers, timeout)
time.AfterFunc(timeout, func() {
h.closeWorkers(workers)
})
return nil
}
func (h *DynamicInboundHandler) monitor() {
timer := time.NewTicker(time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()))
defer timer.Stop()
for {
select {
case <-h.ctx.Done():
return
case <-timer.C:
h.refresh()
}
}
}
func (h *DynamicInboundHandler) Start() error {
err := h.refresh()
go h.monitor()
return err
return h.task.Start()
}
func (h *DynamicInboundHandler) Close() {
h.cancel()
func (h *DynamicInboundHandler) Close() error {
return h.task.Close()
}
func (h *DynamicInboundHandler) GetRandomInboundProxy() (proxy.Inbound, net.Port, int) {
func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) {
h.workerMutex.RLock()
defer h.workerMutex.RUnlock()
@ -174,3 +167,7 @@ func (h *DynamicInboundHandler) GetRandomInboundProxy() (proxy.Inbound, net.Port
expire := h.receiverConfig.AllocationStrategy.GetRefreshValue() - uint32(time.Since(h.lastRefresh)/time.Minute)
return w.Proxy(), w.Port(), int(expire)
}
func (h *DynamicInboundHandler) Tag() string {
return h.tag
}

View File

@ -4,65 +4,60 @@ package inbound
import (
"context"
"sync"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common"
)
// Manager is to manage all inbound handlers.
type Manager struct {
handlers []proxyman.InboundHandler
taggedHandlers map[string]proxyman.InboundHandler
access sync.RWMutex
untaggedHandler []core.InboundHandler
taggedHandlers map[string]core.InboundHandler
running bool
}
// New returns a new Manager for inbound handlers.
func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) {
return &Manager{
taggedHandlers: make(map[string]proxyman.InboundHandler),
}, nil
m := &Manager{
taggedHandlers: make(map[string]core.InboundHandler),
}
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context")
}
if err := v.RegisterFeature((*core.InboundHandlerManager)(nil), m); err != nil {
return nil, newError("unable to register InboundHandlerManager").Base(err)
}
return m, nil
}
func (m *Manager) AddHandler(ctx context.Context, config *proxyman.InboundHandlerConfig) error {
rawReceiverSettings, err := config.ReceiverSettings.GetInstance()
if err != nil {
return err
}
receiverSettings, ok := rawReceiverSettings.(*proxyman.ReceiverConfig)
if !ok {
return newError("not a ReceiverConfig").AtError()
}
proxySettings, err := config.ProxySettings.GetInstance()
if err != nil {
return err
}
var handler proxyman.InboundHandler
tag := config.Tag
allocStrategy := receiverSettings.AllocationStrategy
if allocStrategy == nil || allocStrategy.Type == proxyman.AllocationStrategy_Always {
h, err := NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings)
if err != nil {
return err
}
handler = h
} else if allocStrategy.Type == proxyman.AllocationStrategy_Random {
h, err := NewDynamicInboundHandler(ctx, tag, receiverSettings, proxySettings)
if err != nil {
return err
}
handler = h
}
// AddHandler implements core.InboundHandlerManager.
func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) error {
m.access.Lock()
defer m.access.Unlock()
if handler == nil {
return newError("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError()
}
m.handlers = append(m.handlers, handler)
tag := handler.Tag()
if len(tag) > 0 {
m.taggedHandlers[tag] = handler
} else {
m.untaggedHandler = append(m.untaggedHandler, handler)
}
if m.running {
return handler.Start()
}
return nil
}
func (m *Manager) GetHandler(ctx context.Context, tag string) (proxyman.InboundHandler, error) {
// GetHandler returns core.InboundHandlerManager.
func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) {
m.access.RLock()
defer m.access.RUnlock()
handler, found := m.taggedHandlers[tag]
if !found {
return nil, newError("handler not found: ", tag)
@ -70,8 +65,36 @@ func (m *Manager) GetHandler(ctx context.Context, tag string) (proxyman.InboundH
return handler, nil
}
func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
if len(tag) == 0 {
return core.ErrNoClue
}
m.access.Lock()
defer m.access.Unlock()
if handler, found := m.taggedHandlers[tag]; found {
handler.Close()
delete(m.taggedHandlers, tag)
return nil
}
return core.ErrNoClue
}
func (m *Manager) Start() error {
for _, handler := range m.handlers {
m.access.Lock()
defer m.access.Unlock()
m.running = true
for _, handler := range m.taggedHandlers {
if err := handler.Start(); err != nil {
return err
}
}
for _, handler := range m.untaggedHandler {
if err := handler.Start(); err != nil {
return err
}
@ -79,18 +102,52 @@ func (m *Manager) Start() error {
return nil
}
func (m *Manager) Close() {
for _, handler := range m.handlers {
func (m *Manager) Close() error {
m.access.Lock()
defer m.access.Unlock()
m.running = false
for _, handler := range m.taggedHandlers {
handler.Close()
}
for _, handler := range m.untaggedHandler {
handler.Close()
}
return nil
}
func (m *Manager) Interface() interface{} {
return (*proxyman.InboundHandlerManager)(nil)
func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (core.InboundHandler, error) {
rawReceiverSettings, err := config.ReceiverSettings.GetInstance()
if err != nil {
return nil, err
}
receiverSettings, ok := rawReceiverSettings.(*proxyman.ReceiverConfig)
if !ok {
return nil, newError("not a ReceiverConfig").AtError()
}
proxySettings, err := config.ProxySettings.GetInstance()
if err != nil {
return nil, err
}
tag := config.Tag
allocStrategy := receiverSettings.AllocationStrategy
if allocStrategy == nil || allocStrategy.Type == proxyman.AllocationStrategy_Always {
return NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings)
}
if allocStrategy.Type == proxyman.AllocationStrategy_Random {
return NewDynamicInboundHandler(ctx, tag, receiverSettings, proxySettings)
}
return nil, newError("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError()
}
func init() {
common.Must(common.RegisterConfig((*proxyman.InboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*proxyman.InboundConfig))
}))
common.Must(common.RegisterConfig((*core.InboundHandlerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewHandler(ctx, config.(*core.InboundHandlerConfig))
}))
}

View File

@ -7,10 +7,12 @@ import (
"sync/atomic"
"time"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
"v2ray.com/core/common/signal"
"v2ray.com/core/proxy"
"v2ray.com/core/transport/internet"
"v2ray.com/core/transport/internet/tcp"
@ -19,7 +21,7 @@ import (
type worker interface {
Start() error
Close()
Close() error
Port() net.Port
Proxy() proxy.Inbound
}
@ -31,16 +33,14 @@ type tcpWorker struct {
stream *internet.StreamConfig
recvOrigDest bool
tag string
dispatcher dispatcher.Interface
dispatcher core.Dispatcher
sniffers []proxyman.KnownProtocols
ctx context.Context
cancel context.CancelFunc
hub internet.Listener
hub internet.Listener
}
func (w *tcpWorker) callback(conn internet.Connection) {
ctx, cancel := context.WithCancel(w.ctx)
ctx, cancel := context.WithCancel(context.Background())
if w.recvOrigDest {
dest, err := tcp.GetOriginalDestination(conn)
if err != nil {
@ -70,45 +70,24 @@ func (w *tcpWorker) Proxy() proxy.Inbound {
}
func (w *tcpWorker) Start() error {
ctx, cancel := context.WithCancel(context.Background())
w.ctx = ctx
w.cancel = cancel
ctx = internet.ContextWithStreamSettings(ctx, w.stream)
conns := make(chan internet.Connection, 16)
hub, err := internet.ListenTCP(ctx, w.address, w.port, conns)
ctx := internet.ContextWithStreamSettings(context.Background(), w.stream)
hub, err := internet.ListenTCP(ctx, w.address, w.port, func(conn internet.Connection) {
go w.callback(conn)
})
if err != nil {
return newError("failed to listen TCP on ", w.port).AtWarning().Base(err)
}
go w.handleConnections(conns)
w.hub = hub
return nil
}
func (w *tcpWorker) handleConnections(conns <-chan internet.Connection) {
for {
select {
case <-w.ctx.Done():
w.hub.Close()
L:
for {
select {
case conn := <-conns:
conn.Close()
default:
break L
}
}
return
case conn := <-conns:
go w.callback(conn)
}
}
}
func (w *tcpWorker) Close() {
func (w *tcpWorker) Close() error {
if w.hub != nil {
w.cancel()
common.Close(w.hub)
common.Close(w.proxy)
}
return nil
}
func (w *tcpWorker) Port() net.Port {
@ -121,7 +100,7 @@ type udpConn struct {
output func([]byte) (int, error)
remote net.Addr
local net.Addr
cancel context.CancelFunc
done *signal.Done
}
func (c *udpConn) updateActivity() {
@ -129,13 +108,14 @@ func (c *udpConn) updateActivity() {
}
func (c *udpConn) Read(buf []byte) (int, error) {
in, open := <-c.input
if !open {
select {
case in := <-c.input:
defer in.Release()
c.updateActivity()
return copy(buf, in.Bytes()), nil
case <-c.done.C():
return 0, io.EOF
}
defer in.Release()
c.updateActivity()
return copy(buf, in.Bytes()), nil
}
// Write implements io.Writer.
@ -148,6 +128,7 @@ func (c *udpConn) Write(buf []byte) (int, error) {
}
func (c *udpConn) Close() error {
common.Close(c.done)
return nil
}
@ -171,7 +152,7 @@ func (*udpConn) SetWriteDeadline(time.Time) error {
return nil
}
type connId struct {
type connID struct {
src net.Destination
dest net.Destination
}
@ -185,14 +166,13 @@ type udpWorker struct {
port net.Port
recvOrigDest bool
tag string
dispatcher dispatcher.Interface
dispatcher core.Dispatcher
ctx context.Context
cancel context.CancelFunc
activeConn map[connId]*udpConn
done *signal.Done
activeConn map[connID]*udpConn
}
func (w *udpWorker) getConnection(id connId) (*udpConn, bool) {
func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
w.Lock()
defer w.Unlock()
@ -213,6 +193,7 @@ func (w *udpWorker) getConnection(id connId) (*udpConn, bool) {
IP: w.address.IP(),
Port: int(w.port),
},
done: signal.NewDone(),
}
w.activeConn[id] = conn
@ -221,22 +202,22 @@ func (w *udpWorker) getConnection(id connId) (*udpConn, bool) {
}
func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) {
id := connId{
id := connID{
src: source,
dest: originalDest,
}
conn, existing := w.getConnection(id)
select {
case conn.input <- b:
case <-conn.done.C():
b.Release()
default:
b.Release()
}
if !existing {
go func() {
ctx := w.ctx
ctx, cancel := context.WithCancel(ctx)
conn.cancel = cancel
ctx := context.Background()
if originalDest.IsValid() {
ctx = proxy.ContextWithOriginalTarget(ctx, originalDest)
}
@ -248,23 +229,21 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
newError("connection ends").Base(err).WriteToLog()
}
conn.Close()
w.removeConn(id)
cancel()
}()
}
}
func (w *udpWorker) removeConn(id connId) {
func (w *udpWorker) removeConn(id connID) {
w.Lock()
delete(w.activeConn, id)
w.Unlock()
}
func (w *udpWorker) Start() error {
w.activeConn = make(map[connId]*udpConn, 16)
ctx, cancel := context.WithCancel(context.Background())
w.ctx = ctx
w.cancel = cancel
w.activeConn = make(map[connID]*udpConn, 16)
w.done = signal.NewDone()
h, err := udp.ListenUDP(w.address, w.port, udp.ListenOption{
Callback: w.callback,
ReceiveOriginalDest: w.recvOrigDest,
@ -277,11 +256,13 @@ func (w *udpWorker) Start() error {
return nil
}
func (w *udpWorker) Close() {
func (w *udpWorker) Close() error {
if w.hub != nil {
w.hub.Close()
w.cancel()
w.done.Close()
common.Close(w.proxy)
}
return nil
}
func (w *udpWorker) monitor() {
@ -290,7 +271,7 @@ func (w *udpWorker) monitor() {
for {
select {
case <-w.ctx.Done():
case <-w.done.C():
return
case <-timer.C:
nowSec := time.Now().Unix()
@ -298,7 +279,7 @@ func (w *udpWorker) monitor() {
for addr, conn := range w.activeConn {
if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 8 {
delete(w.activeConn, addr)
conn.cancel()
conn.Close()
}
}
w.Unlock()

View File

@ -8,13 +8,13 @@ import (
"sync"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/signal"
"v2ray.com/core/proxy"
"v2ray.com/core/transport/ray"
)
@ -75,8 +75,7 @@ func (m *ClientManager) onClientFinish() {
type Client struct {
sessionManager *SessionManager
inboundRay ray.InboundRay
ctx context.Context
cancel context.CancelFunc
done *signal.Done
manager *ClientManager
concurrency uint32
}
@ -86,26 +85,26 @@ var muxCoolPort = net.Port(9527)
// NewClient creates a new mux.Client.
func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client, error) {
ctx, cancel := context.WithCancel(context.Background())
ctx = proxy.ContextWithTarget(ctx, net.TCPDestination(muxCoolAddress, muxCoolPort))
ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(muxCoolAddress, muxCoolPort))
ctx, cancel := context.WithCancel(ctx)
pipe := ray.NewRay(ctx)
go func() {
if err := p.Process(ctx, pipe, dialer); err != nil {
cancel()
errors.New("failed to handler mux client connection").Base(err).WriteToLog()
}
}()
c := &Client{
sessionManager: NewSessionManager(),
inboundRay: pipe,
ctx: ctx,
cancel: cancel,
done: signal.NewDone(),
manager: m,
concurrency: m.config.Concurrency,
}
go func() {
if err := p.Process(ctx, pipe, dialer); err != nil {
errors.New("failed to handler mux client connection").Base(err).WriteToLog()
}
c.done.Close()
cancel()
}()
go c.fetchOutput()
go c.monitor()
return c, nil
@ -113,12 +112,7 @@ func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client
// Closed returns true if this Client is closed.
func (m *Client) Closed() bool {
select {
case <-m.ctx.Done():
return true
default:
return false
}
return m.done.Done()
}
func (m *Client) monitor() {
@ -129,7 +123,7 @@ func (m *Client) monitor() {
for {
select {
case <-m.ctx.Done():
case <-m.done.C():
m.sessionManager.Close()
m.inboundRay.InboundInput().Close()
m.inboundRay.InboundOutput().CloseError()
@ -137,7 +131,8 @@ func (m *Client) monitor() {
case <-timer.C:
size := m.sessionManager.Size()
if size == 0 && m.sessionManager.CloseIfNoSession() {
m.cancel()
m.done.Close()
return
}
}
}
@ -171,10 +166,8 @@ func (m *Client) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) bool
return false
}
select {
case <-m.ctx.Done():
if m.done.Done() {
return false
default:
}
s := sm.Allocate()
@ -227,7 +220,7 @@ func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader
}
func (m *Client) fetchOutput() {
defer m.cancel()
defer m.done.Close()
reader := buf.NewBufferedReader(m.inboundRay.InboundOutput())
@ -262,21 +255,14 @@ func (m *Client) fetchOutput() {
}
type Server struct {
dispatcher dispatcher.Interface
dispatcher core.Dispatcher
}
// NewServer creates a new mux.Server.
func NewServer(ctx context.Context) *Server {
s := &Server{}
space := app.SpaceFromContext(ctx)
space.On(app.SpaceInitializing, func(interface{}) error {
d := dispatcher.FromSpace(space)
if d == nil {
return newError("no dispatcher in space")
}
s.dispatcher = d
return nil
})
s := &Server{
dispatcher: core.FromContext(ctx).Dispatcher(),
}
return s
}
@ -295,8 +281,16 @@ func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (ray.Inboun
return ray, nil
}
func (s *Server) Start() error {
return nil
}
func (s *Server) Close() error {
return nil
}
type ServerWorker struct {
dispatcher dispatcher.Interface
dispatcher core.Dispatcher
outboundRay ray.OutboundRay
sessionManager *SessionManager
}

View File

@ -103,12 +103,12 @@ func (m *SessionManager) CloseIfNoSession() bool {
return true
}
func (m *SessionManager) Close() {
func (m *SessionManager) Close() error {
m.Lock()
defer m.Unlock()
if m.closed {
return
return nil
}
m.closed = true
@ -119,6 +119,7 @@ func (m *SessionManager) Close() {
}
m.sessions = nil
return nil
}
// Session represents a client connection in a Mux connection.
@ -131,10 +132,11 @@ type Session struct {
}
// Close closes all resources associated with this session.
func (s *Session) Close() {
func (s *Session) Close() error {
s.output.Close()
s.input.Close()
s.parent.Remove(s.ID)
return nil
}
// NewReader creates a buf.Reader based on the transfer type of this Session.

View File

@ -100,7 +100,7 @@ func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
return nil
}
func (w *Writer) Close() {
func (w *Writer) Close() error {
meta := FrameMetadata{
SessionID: w.id,
SessionStatus: SessionStatusEnd,
@ -110,4 +110,5 @@ func (w *Writer) Close() {
common.Must(frame.Reset(meta.AsSupplier()))
w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame))
return nil
}

View File

@ -3,12 +3,11 @@ package outbound
import (
"context"
"io"
"time"
"v2ray.com/core/app"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/app/proxyman/mux"
"v2ray.com/core/common/buf"
"v2ray.com/core/common"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/net"
"v2ray.com/core/proxy"
@ -17,29 +16,22 @@ import (
)
type Handler struct {
config *proxyman.OutboundHandlerConfig
config *core.OutboundHandlerConfig
senderSettings *proxyman.SenderConfig
proxy proxy.Outbound
outboundManager proxyman.OutboundHandlerManager
outboundManager core.OutboundHandlerManager
mux *mux.ClientManager
}
func NewHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) (*Handler, error) {
func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (*Handler, error) {
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context")
}
h := &Handler{
config: config,
config: config,
outboundManager: v.OutboundHandlerManager(),
}
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context")
}
space.On(app.SpaceInitializing, func(interface{}) error {
ohm := proxyman.OutboundHandlerManagerFromSpace(space)
if ohm == nil {
return newError("no OutboundManager in space")
}
h.outboundManager = ohm
return nil
})
if config.SenderSettings != nil {
senderSettings, err := config.SenderSettings.GetInstance()
@ -54,11 +46,21 @@ func NewHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) (*H
}
}
proxyHandler, err := config.GetProxyHandler(ctx)
proxyConfig, err := config.ProxySettings.GetInstance()
if err != nil {
return nil, err
}
rawProxyHandler, err := common.CreateObject(ctx, proxyConfig)
if err != nil {
return nil, err
}
proxyHandler, ok := rawProxyHandler.(proxy.Outbound)
if !ok {
return nil, newError("not an outbound handler")
}
if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil && h.senderSettings.MultiplexSettings.Enabled {
config := h.senderSettings.MultiplexSettings
if config.Concurrency < 1 || config.Concurrency > 1024 {
@ -71,6 +73,11 @@ func NewHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) (*H
return h, nil
}
// Tag implements core.OutboundHandler.
func (h *Handler) Tag() string {
return h.config.Tag
}
// Dispatch implements proxy.Outbound.Dispatch.
func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) {
if h.mux != nil {
@ -103,7 +110,7 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn
ctx = proxy.ContextWithTarget(ctx, dest)
stream := ray.NewRay(ctx)
go handler.Dispatch(ctx, stream)
return NewConnection(stream), nil
return ray.NewConnection(stream.InboundOutput(), stream.InboundInput()), nil
}
newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog()
@ -121,98 +128,18 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn
return internet.Dial(ctx, dest)
}
var (
_ buf.Reader = (*Connection)(nil)
_ buf.Writer = (*Connection)(nil)
)
type Connection struct {
stream ray.Ray
closed bool
localAddr net.Addr
remoteAddr net.Addr
reader *buf.BufferedReader
writer buf.Writer
// GetOutbound implements proxy.GetOutbound.
func (h *Handler) GetOutbound() proxy.Outbound {
return h.proxy
}
func NewConnection(stream ray.Ray) *Connection {
return &Connection{
stream: stream,
localAddr: &net.TCPAddr{
IP: []byte{0, 0, 0, 0},
Port: 0,
},
remoteAddr: &net.TCPAddr{
IP: []byte{0, 0, 0, 0},
Port: 0,
},
reader: buf.NewBufferedReader(stream.InboundOutput()),
writer: stream.InboundInput(),
}
}
// Read implements net.Conn.Read().
func (v *Connection) Read(b []byte) (int, error) {
if v.closed {
return 0, io.EOF
}
return v.reader.Read(b)
}
func (v *Connection) ReadMultiBuffer() (buf.MultiBuffer, error) {
return v.reader.ReadMultiBuffer()
}
// Write implements net.Conn.Write().
func (v *Connection) Write(b []byte) (int, error) {
if v.closed {
return 0, io.ErrClosedPipe
}
l := len(b)
mb := buf.NewMultiBufferCap(l/buf.Size + 1)
mb.Write(b)
return l, v.writer.WriteMultiBuffer(mb)
}
func (v *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error {
if v.closed {
return io.ErrClosedPipe
}
return v.writer.WriteMultiBuffer(mb)
}
// Close implements net.Conn.Close().
func (v *Connection) Close() error {
v.closed = true
v.stream.InboundInput().Close()
v.stream.InboundOutput().CloseError()
// Start implements common.Runnable.
func (h *Handler) Start() error {
return nil
}
// LocalAddr implements net.Conn.LocalAddr().
func (v *Connection) LocalAddr() net.Addr {
return v.localAddr
}
// RemoteAddr implements net.Conn.RemoteAddr().
func (v *Connection) RemoteAddr() net.Addr {
return v.remoteAddr
}
// SetDeadline implements net.Conn.SetDeadline().
func (v *Connection) SetDeadline(t time.Time) error {
return nil
}
// SetReadDeadline implements net.Conn.SetReadDeadline().
func (v *Connection) SetReadDeadline(t time.Time) error {
return nil
}
// SetWriteDeadline implement net.Conn.SetWriteDeadline().
func (v *Connection) SetWriteDeadline(t time.Time) error {
// Close implements common.Runnable.
func (h *Handler) Close() error {
common.Close(h.mux)
return nil
}

View File

@ -0,0 +1,16 @@
package outbound_test
import (
"testing"
"v2ray.com/core"
. "v2ray.com/core/app/proxyman/outbound"
. "v2ray.com/ext/assert"
)
func TestInterfaces(t *testing.T) {
assert := With(t)
assert((*Handler)(nil), Implements, (*core.OutboundHandler)(nil))
assert((*Manager)(nil), Implements, (*core.OutboundHandlerManager)(nil))
}

View File

@ -6,67 +6,130 @@ import (
"context"
"sync"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common"
)
// Manager is to manage all outbound handlers.
type Manager struct {
sync.RWMutex
defaultHandler *Handler
taggedHandler map[string]*Handler
access sync.RWMutex
defaultHandler core.OutboundHandler
taggedHandler map[string]core.OutboundHandler
untaggedHandlers []core.OutboundHandler
running bool
}
// New creates a new Manager.
func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) {
return &Manager{
taggedHandler: make(map[string]*Handler),
}, nil
m := &Manager{
taggedHandler: make(map[string]core.OutboundHandler),
}
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context")
}
if err := v.RegisterFeature((*core.OutboundHandlerManager)(nil), m); err != nil {
return nil, newError("unable to register OutboundHandlerManager").Base(err)
}
return m, nil
}
// Interface implements Application.Interface.
func (*Manager) Interface() interface{} {
return (*proxyman.OutboundHandlerManager)(nil)
// Start implements core.Feature
func (m *Manager) Start() error {
m.access.Lock()
defer m.access.Unlock()
m.running = true
for _, h := range m.taggedHandler {
if err := h.Start(); err != nil {
return err
}
}
for _, h := range m.untaggedHandlers {
if err := h.Start(); err != nil {
return err
}
}
return nil
}
// Start implements Application.Start
func (*Manager) Start() error { return nil }
// Close implements core.Feature
func (m *Manager) Close() error {
m.access.Lock()
defer m.access.Unlock()
// Close implements Application.Close
func (*Manager) Close() {}
m.running = false
for _, h := range m.taggedHandler {
h.Close()
}
for _, h := range m.untaggedHandlers {
h.Close()
}
return nil
}
// GetDefaultHandler implements core.OutboundHandlerManager.
func (m *Manager) GetDefaultHandler() core.OutboundHandler {
m.access.RLock()
defer m.access.RUnlock()
func (m *Manager) GetDefaultHandler() proxyman.OutboundHandler {
m.RLock()
defer m.RUnlock()
if m.defaultHandler == nil {
return nil
}
return m.defaultHandler
}
func (m *Manager) GetHandler(tag string) proxyman.OutboundHandler {
m.RLock()
defer m.RUnlock()
// GetHandler implements core.OutboundHandlerManager.
func (m *Manager) GetHandler(tag string) core.OutboundHandler {
m.access.RLock()
defer m.access.RUnlock()
if handler, found := m.taggedHandler[tag]; found {
return handler
}
return nil
}
func (m *Manager) AddHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) error {
m.Lock()
defer m.Unlock()
// AddHandler implements core.OutboundHandlerManager.
func (m *Manager) AddHandler(ctx context.Context, handler core.OutboundHandler) error {
m.access.Lock()
defer m.access.Unlock()
handler, err := NewHandler(ctx, config)
if err != nil {
return err
}
if m.defaultHandler == nil {
m.defaultHandler = handler
}
if len(config.Tag) > 0 {
m.taggedHandler[config.Tag] = handler
tag := handler.Tag()
if len(tag) > 0 {
m.taggedHandler[tag] = handler
} else {
m.untaggedHandlers = append(m.untaggedHandlers, handler)
}
if m.running {
return handler.Start()
}
return nil
}
// RemoveHandler implements core.OutboundHandlerManager.
func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
if len(tag) == 0 {
return core.ErrNoClue
}
m.access.Lock()
defer m.access.Unlock()
delete(m.taggedHandler, tag)
if m.defaultHandler.Tag() == tag {
m.defaultHandler = nil
}
return nil
@ -76,4 +139,7 @@ func init() {
common.Must(common.RegisterConfig((*proxyman.OutboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*proxyman.OutboundConfig))
}))
common.Must(common.RegisterConfig((*core.OutboundHandlerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewHandler(ctx, config.(*core.OutboundHandlerConfig))
}))
}

View File

@ -5,52 +5,8 @@ package proxyman
import (
"context"
"v2ray.com/core/app"
"v2ray.com/core/common/net"
"v2ray.com/core/proxy"
"v2ray.com/core/transport/ray"
)
type InboundHandlerManager interface {
GetHandler(ctx context.Context, tag string) (InboundHandler, error)
AddHandler(ctx context.Context, config *InboundHandlerConfig) error
}
type InboundHandler interface {
Start() error
Close()
// For migration
GetRandomInboundProxy() (proxy.Inbound, net.Port, int)
}
type OutboundHandlerManager interface {
GetHandler(tag string) OutboundHandler
GetDefaultHandler() OutboundHandler
AddHandler(ctx context.Context, config *OutboundHandlerConfig) error
}
type OutboundHandler interface {
Dispatch(ctx context.Context, outboundRay ray.OutboundRay)
}
func InboundHandlerManagerFromSpace(space app.Space) InboundHandlerManager {
app := space.GetApplication((*InboundHandlerManager)(nil))
if app == nil {
return nil
}
return app.(InboundHandlerManager)
}
func OutboundHandlerManagerFromSpace(space app.Space) OutboundHandlerManager {
app := space.GetApplication((*OutboundHandlerManager)(nil))
if app == nil {
return nil
}
return app.(OutboundHandlerManager)
}
type key int
const (

View File

@ -5,46 +5,47 @@ package router
import (
"context"
"v2ray.com/core/app"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/net"
"v2ray.com/core/proxy"
)
var (
ErrNoRuleApplicable = newError("No rule applicable")
)
type Router struct {
domainStrategy Config_DomainStrategy
rules []Rule
dns core.DNSClient
}
func NewRouter(ctx context.Context, config *Config) (*Router, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context")
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context")
}
r := &Router{
domainStrategy: config.DomainStrategy,
rules: make([]Rule, len(config.Rule)),
dns: v.DNSClient(),
}
space.On(app.SpaceInitializing, func(interface{}) error {
for idx, rule := range config.Rule {
r.rules[idx].Tag = rule.Tag
cond, err := rule.BuildCondition()
if err != nil {
return err
}
r.rules[idx].Condition = cond
for idx, rule := range config.Rule {
r.rules[idx].Tag = rule.Tag
cond, err := rule.BuildCondition()
if err != nil {
return nil, err
}
return nil
})
r.rules[idx].Condition = cond
}
if err := v.RegisterFeature((*core.Router)(nil), r); err != nil {
return nil, newError("unable to register Router").Base(err)
}
return r, nil
}
type ipResolver struct {
dns core.DNSClient
ip []net.Address
domain string
resolved bool
@ -57,7 +58,7 @@ func (r *ipResolver) Resolve() []net.Address {
newError("looking for IP for domain: ", r.domain).WriteToLog()
r.resolved = true
ips, err := net.LookupIP(r.domain)
ips, err := r.dns.LookupIP(r.domain)
if err != nil {
newError("failed to get IP address").Base(err).WriteToLog()
}
@ -71,8 +72,10 @@ func (r *ipResolver) Resolve() []net.Address {
return r.ip
}
func (r *Router) TakeDetour(ctx context.Context) (string, error) {
resolver := &ipResolver{}
func (r *Router) PickRoute(ctx context.Context) (string, error) {
resolver := &ipResolver{
dns: r.dns,
}
if r.domainStrategy == Config_IpOnDemand {
if dest, ok := proxy.TargetFromContext(ctx); ok && dest.Address.Family().IsDomain() {
resolver.domain = dest.Address.Domain()
@ -88,7 +91,7 @@ func (r *Router) TakeDetour(ctx context.Context) (string, error) {
dest, ok := proxy.TargetFromContext(ctx)
if !ok {
return "", ErrNoRuleApplicable
return "", core.ErrNoClue
}
if r.domainStrategy == Config_IpIfNonMatch && dest.Address.Family().IsDomain() {
@ -104,25 +107,15 @@ func (r *Router) TakeDetour(ctx context.Context) (string, error) {
}
}
return "", ErrNoRuleApplicable
}
func (*Router) Interface() interface{} {
return (*Router)(nil)
return "", core.ErrNoClue
}
func (*Router) Start() error {
return nil
}
func (*Router) Close() {}
func FromSpace(space app.Space) *Router {
app := space.GetApplication((*Router)(nil))
if app == nil {
return nil
}
return app.(*Router)
func (*Router) Close() error {
return nil
}
func init() {

View File

@ -4,13 +4,14 @@ import (
"context"
"testing"
"v2ray.com/core/app"
"v2ray.com/core"
"v2ray.com/core/app/dispatcher"
_ "v2ray.com/core/app/dispatcher/impl"
"v2ray.com/core/app/proxyman"
_ "v2ray.com/core/app/proxyman/outbound"
. "v2ray.com/core/app/router"
"v2ray.com/core/common"
"v2ray.com/core/common/net"
"v2ray.com/core/common/serial"
"v2ray.com/core/proxy"
. "v2ray.com/ext/assert"
)
@ -18,28 +19,30 @@ import (
func TestSimpleRouter(t *testing.T) {
assert := With(t)
config := &Config{
Rule: []*RoutingRule{
{
Tag: "test",
NetworkList: &net.NetworkList{
Network: []net.Network{net.Network_TCP},
config := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{
Rule: []*RoutingRule{
{
Tag: "test",
NetworkList: &net.NetworkList{
Network: []net.Network{net.Network_TCP},
},
},
},
},
}),
serial.ToTypedMessage(&dispatcher.Config{}),
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
},
}
space := app.NewSpace()
ctx := app.ContextWithSpace(context.Background(), space)
assert(app.AddApplicationToSpace(ctx, new(dispatcher.Config)), IsNil)
assert(app.AddApplicationToSpace(ctx, new(proxyman.OutboundConfig)), IsNil)
assert(app.AddApplicationToSpace(ctx, config), IsNil)
assert(space.Initialize(), IsNil)
v, err := core.New(config)
common.Must(err)
r := FromSpace(space)
r := v.Router()
ctx = proxy.ContextWithTarget(ctx, net.TCPDestination(net.DomainAddress("v2ray.com"), 80))
tag, err := r.TakeDetour(ctx)
ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(net.DomainAddress("v2ray.com"), 80))
tag, err := r.PickRoute(ctx)
assert(err, IsNil)
assert(tag, Equals, "test")
}

View File

@ -1,132 +0,0 @@
package app
import (
"context"
"reflect"
"v2ray.com/core/common"
"v2ray.com/core/common/event"
)
// Application is a component that runs in Space.
type Application interface {
Interface() interface{}
Start() error
Close()
}
// CreateAppFromConfig creates an Application based on its config. Application must have been registered.
func CreateAppFromConfig(ctx context.Context, config interface{}) (Application, error) {
application, err := common.CreateObject(ctx, config)
if err != nil {
return nil, err
}
switch a := application.(type) {
case Application:
return a, nil
default:
return nil, newError("not an application")
}
}
// A Space contains all apps that may be available in a V2Ray runtime.
type Space interface {
event.Registry
GetApplication(appInterface interface{}) Application
AddApplication(application Application) error
Initialize() error
Start() error
Close()
}
const (
// SpaceInitializing is an event to be fired when Space is being initialized.
SpaceInitializing event.Event = iota
)
type spaceImpl struct {
event.Listener
cache map[reflect.Type]Application
initialized bool
}
// NewSpace creates a new Space.
func NewSpace() Space {
return &spaceImpl{
cache: make(map[reflect.Type]Application),
}
}
func (s *spaceImpl) On(e event.Event, h event.Handler) {
if e == SpaceInitializing && s.initialized {
_ = h(nil) // Ignore error
return
}
s.Listener.On(e, h)
}
func (s *spaceImpl) Initialize() error {
if s.initialized {
return nil
}
s.initialized = true
return s.Fire(SpaceInitializing, nil)
}
func (s *spaceImpl) GetApplication(appInterface interface{}) Application {
if s == nil {
return nil
}
appType := reflect.TypeOf(appInterface)
return s.cache[appType]
}
func (s *spaceImpl) AddApplication(app Application) error {
if s == nil {
return newError("nil space").AtError()
}
appType := reflect.TypeOf(app.Interface())
s.cache[appType] = app
return nil
}
func (s *spaceImpl) Start() error {
for _, app := range s.cache {
if err := app.Start(); err != nil {
return err
}
}
return nil
}
func (s *spaceImpl) Close() {
for _, app := range s.cache {
app.Close()
}
}
type contextKey int
const (
spaceKey = contextKey(0)
)
func AddApplicationToSpace(ctx context.Context, appConfig interface{}) error {
space := SpaceFromContext(ctx)
if space == nil {
return newError("no space in context").AtError()
}
application, err := CreateAppFromConfig(ctx, appConfig)
if err != nil {
return err
}
return space.AddApplication(application)
}
func SpaceFromContext(ctx context.Context) Space {
return ctx.Value(spaceKey).(Space)
}
func ContextWithSpace(ctx context.Context, space Space) context.Context {
return context.WithValue(ctx, spaceKey, space)
}

59
clock.go Normal file
View File

@ -0,0 +1,59 @@
package core
import (
"sync"
"time"
)
// Clock is a V2Ray feature that returns current time.
type Clock interface {
Feature
// Now returns current time.
Now() time.Time
}
type syncClock struct {
sync.RWMutex
Clock
}
func (c *syncClock) Now() time.Time {
c.RLock()
defer c.RUnlock()
if c.Clock == nil {
return time.Now()
}
return c.Clock.Now()
}
func (c *syncClock) Start() error {
c.RLock()
defer c.RUnlock()
if c.Clock == nil {
return nil
}
return c.Clock.Start()
}
func (c *syncClock) Close() error {
c.RLock()
defer c.RUnlock()
if c.Clock == nil {
return nil
}
return c.Clock.Close()
}
func (c *syncClock) Set(clock Clock) {
c.Lock()
defer c.Unlock()
c.Clock = clock
}

44
commander.go Normal file
View File

@ -0,0 +1,44 @@
package core
import (
"sync"
)
// Commander is a feature that accepts commands from external source.
type Commander interface {
Feature
}
type syncCommander struct {
sync.RWMutex
Commander
}
func (c *syncCommander) Start() error {
c.RLock()
defer c.RUnlock()
if c.Commander == nil {
return nil
}
return c.Commander.Start()
}
func (c *syncCommander) Close() error {
c.RLock()
defer c.RUnlock()
if c.Commander == nil {
return nil
}
return c.Commander.Close()
}
func (c *syncCommander) Set(commander Commander) {
c.Lock()
defer c.Unlock()
c.Commander = commander
}

View File

@ -1,46 +0,0 @@
package event
import "sync"
type Event uint16
type Handler func(data interface{}) error
type Registry interface {
On(Event, Handler)
}
type Listener struct {
sync.RWMutex
events map[Event][]Handler
}
func (l *Listener) On(e Event, h Handler) {
l.Lock()
defer l.Unlock()
if l.events == nil {
l.events = make(map[Event][]Handler)
}
handlers := l.events[e]
handlers = append(handlers, h)
l.events[e] = handlers
}
func (l *Listener) Fire(e Event, data interface{}) error {
l.RLock()
defer l.RUnlock()
if l.events == nil {
return nil
}
for _, h := range l.events[e] {
if err := h(data); err != nil {
return err
}
}
return nil
}

23
common/interfaces.go Normal file
View File

@ -0,0 +1,23 @@
package common
// Closable is the interface for objects that can release its resources.
type Closable interface {
// Close release all resources used by this object, including goroutines.
Close() error
}
// Close closes the obj if it is a Closable.
func Close(obj interface{}) error {
if c, ok := obj.(Closable); ok {
return c.Close()
}
return nil
}
// Runnable is the interface for objects that can start to work and stop on demand.
type Runnable interface {
// Start starts the runnable object. Upon the method returning nil, the object begins to function properly.
Start() error
Closable
}

View File

@ -1,8 +1,7 @@
package log
import (
"sync/atomic"
"unsafe"
"sync"
"v2ray.com/core/common/serial"
)
@ -30,12 +29,11 @@ func (m *GeneralMessage) String() string {
// Record writes a message into log stream.
func Record(msg Message) {
h := (*Handler)(atomic.LoadPointer(&logHandler))
(*h).Handle(msg)
logHandler.Handle(msg)
}
var (
logHandler unsafe.Pointer
logHandler syncHandler
)
// RegisterHandler register a new handler as current log handler. Previous registered handler will be discarded.
@ -43,5 +41,26 @@ func RegisterHandler(handler Handler) {
if handler == nil {
panic("Log handler is nil")
}
atomic.StorePointer(&logHandler, unsafe.Pointer(&handler))
logHandler.Set(handler)
}
type syncHandler struct {
sync.RWMutex
Handler
}
func (h *syncHandler) Handle(msg Message) {
h.RLock()
defer h.RUnlock()
if h.Handler != nil {
h.Handler.Handle(msg)
}
}
func (h *syncHandler) Set(handler Handler) {
h.Lock()
defer h.Unlock()
h.Handler = handler
}

View File

@ -1,44 +0,0 @@
package net
import (
"net"
"sync/atomic"
"unsafe"
)
// IPResolver is the interface to resolve host name to IPs.
type IPResolver interface {
LookupIP(host string) ([]net.IP, error)
}
type systemIPResolver int
func (s systemIPResolver) LookupIP(host string) ([]net.IP, error) {
return net.LookupIP(host)
}
const (
systemIPResolverInstance = systemIPResolver(0)
)
// SystemIPResolver returns an IPResolver that resolves IP through underlying system.
func SystemIPResolver() IPResolver {
return systemIPResolverInstance
}
var (
ipResolver unsafe.Pointer
)
func LookupIP(host string) ([]net.IP, error) {
r := (*IPResolver)(atomic.LoadPointer(&ipResolver))
return (*r).LookupIP(host)
}
func RegisterIPResolver(resolver IPResolver) {
atomic.StorePointer(&ipResolver, unsafe.Pointer(&resolver))
}
func init() {
RegisterIPResolver(systemIPResolverInstance)
}

View File

@ -11,6 +11,8 @@ var Listen = net.Listen
var ListenTCP = net.ListenTCP
var ListenUDP = net.ListenUDP
var LookupIP = net.LookupIP
var FileConn = net.FileConn
var ParseIP = net.ParseIP

View File

@ -18,11 +18,14 @@ const (
)
func (c RequestCommand) TransferType() TransferType {
if c == RequestCommandTCP {
switch c {
case RequestCommandTCP, RequestCommandMux:
return TransferTypeStream
case RequestCommandUDP:
return TransferTypePacket
default:
return TransferTypeStream
}
return TransferTypePacket
}
const (
@ -79,7 +82,7 @@ type ResponseHeader struct {
type CommandSwitchAccount struct {
Host net.Address
Port net.Port
ID *uuid.UUID
ID uuid.UUID
Level uint32
AlterIds uint16
ValidMin byte

View File

@ -21,13 +21,13 @@ func DefaultIDHash(key []byte) hash.Hash {
// The ID of en entity, in the form of an UUID.
type ID struct {
uuid *uuid.UUID
uuid uuid.UUID
cmdKey [IDBytesLen]byte
}
// Equals returns true if this ID equals to the other one.
func (id *ID) Equals(another *ID) bool {
return id.uuid.Equals(another.uuid)
return id.uuid.Equals(&(another.uuid))
}
func (id *ID) Bytes() []byte {
@ -38,7 +38,7 @@ func (id *ID) String() string {
return id.uuid.String()
}
func (id *ID) UUID() *uuid.UUID {
func (id *ID) UUID() uuid.UUID {
return id.uuid
}
@ -47,7 +47,7 @@ func (id ID) CmdKey() []byte {
}
// NewID returns an ID with given UUID.
func NewID(uuid *uuid.UUID) *ID {
func NewID(uuid uuid.UUID) *ID {
id := &ID{uuid: uuid}
md5hash := md5.New()
common.Must2(md5hash.Write(uuid.Bytes()))

View File

@ -14,7 +14,7 @@ var _ = math.Inf
type User struct {
Level uint32 `protobuf:"varint,1,opt,name=level" json:"level,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email" json:"email,omitempty"`
// Protocol specific account information.
// Protocol specific account information. Must be the account proto in one of the proxies.
Account *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=account" json:"account,omitempty"`
}

View File

@ -3,4 +3,5 @@ package protocol
type UserValidator interface {
Add(user *User) error
Get(timeHash []byte) (*User, Timestamp, bool)
Remove(email string) bool
}

View File

@ -0,0 +1,12 @@
package router
import (
"context"
"v2ray.com/core/common/net"
"v2ray.com/core/transport/ray"
)
type Dispatcher interface {
Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error)
}

53
common/router/router.go Normal file
View File

@ -0,0 +1,53 @@
package router
import (
"context"
"sync"
)
type Router interface {
Pick(ctx context.Context) (string, bool)
}
type defaultRouter byte
func (defaultRouter) Pick(ctx context.Context) (string, bool) {
return "", false
}
type syncRouter struct {
sync.RWMutex
Router
}
func (r *syncRouter) Pick(ctx context.Context) (string, bool) {
r.RLock()
defer r.RUnlock()
return r.Router.Pick(ctx)
}
func (r *syncRouter) Set(router Router) {
r.Lock()
defer r.Unlock()
r.Router = router
}
var (
routerInstance = &syncRouter{
Router: defaultRouter(0),
}
)
func RegisterRouter(router Router) {
if router == nil {
panic("Router is nil.")
}
routerInstance.Set(router)
}
func Pick(ctx context.Context) (string, bool) {
return routerInstance.Router.Pick(ctx)
}

48
common/signal/done.go Normal file
View File

@ -0,0 +1,48 @@
package signal
import (
"sync"
)
type Done struct {
access sync.Mutex
c chan struct{}
closed bool
}
func NewDone() *Done {
return &Done{
c: make(chan struct{}),
}
}
func (d *Done) Done() bool {
select {
case <-d.c:
return true
default:
return false
}
}
func (d *Done) C() chan struct{} {
return d.c
}
func (d *Done) Wait() {
<-d.c
}
func (d *Done) Close() error {
d.access.Lock()
defer d.access.Unlock()
if d.closed {
return nil
}
d.closed = true
close(d.c)
return nil
}

View File

@ -1,22 +1,22 @@
package signal
type Notifier struct {
c chan bool
c chan struct{}
}
func NewNotifier() *Notifier {
return &Notifier{
c: make(chan bool, 1),
c: make(chan struct{}, 1),
}
}
func (n *Notifier) Signal() {
select {
case n.c <- true:
case n.c <- struct{}{}:
default:
}
}
func (n *Notifier) Wait() <-chan bool {
func (n *Notifier) Wait() <-chan struct{} {
return n.c
}

View File

@ -1,23 +1,23 @@
package signal
type Semaphore struct {
token chan bool
token chan struct{}
}
func NewSemaphore(n int) *Semaphore {
s := &Semaphore{
token: make(chan bool, n),
token: make(chan struct{}, n),
}
for i := 0; i < n; i++ {
s.token <- true
s.token <- struct{}{}
}
return s
}
func (s *Semaphore) Wait() <-chan bool {
func (s *Semaphore) Wait() <-chan struct{} {
return s.token
}
func (s *Semaphore) Signal() {
s.token <- true
s.token <- struct{}{}
}

60
common/signal/task.go Normal file
View File

@ -0,0 +1,60 @@
package signal
import (
"sync"
"time"
)
type PeriodicTask struct {
Interval time.Duration
Execute func() error
access sync.Mutex
timer *time.Timer
closed bool
}
func (t *PeriodicTask) checkedExecute() error {
t.access.Lock()
defer t.access.Unlock()
if t.closed {
return nil
}
if err := t.Execute(); err != nil {
return err
}
t.timer = time.AfterFunc(t.Interval, func() {
t.checkedExecute()
})
return nil
}
func (t *PeriodicTask) Start() error {
t.access.Lock()
t.closed = false
t.access.Unlock()
if err := t.checkedExecute(); err != nil {
t.closed = true
return err
}
return nil
}
func (t *PeriodicTask) Close() error {
t.access.Lock()
defer t.access.Unlock()
t.closed = true
if t.timer != nil {
t.timer.Stop()
t.timer = nil
}
return nil
}

View File

@ -0,0 +1,29 @@
package signal_test
import (
"testing"
"time"
"v2ray.com/core/common"
. "v2ray.com/core/common/signal"
. "v2ray.com/ext/assert"
)
func TestPeriodicTaskStop(t *testing.T) {
assert := With(t)
value := 0
task := &PeriodicTask{
Interval: time.Second * 2,
Execute: func() error {
value++
return nil
},
}
common.Must(task.Start())
time.Sleep(time.Second * 5)
common.Must(task.Close())
assert(value, Equals, 3)
time.Sleep(time.Second * 4)
assert(value, Equals, 3)
}

View File

@ -10,23 +10,30 @@ type ActivityUpdater interface {
}
type ActivityTimer struct {
updated chan bool
updated chan struct{}
timeout chan time.Duration
closing chan struct{}
}
func (t *ActivityTimer) Update() {
select {
case t.updated <- true:
case t.updated <- struct{}{}:
default:
}
}
func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
t.timeout <- timeout
select {
case <-t.closing:
case t.timeout <- timeout:
}
}
func (t *ActivityTimer) run(ctx context.Context, cancel context.CancelFunc) {
defer cancel()
defer func() {
cancel()
close(t.closing)
}()
timeout := <-t.timeout
if timeout == 0 {
@ -65,7 +72,8 @@ func (t *ActivityTimer) run(ctx context.Context, cancel context.CancelFunc) {
func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {
timer := &ActivityTimer{
timeout: make(chan time.Duration, 1),
updated: make(chan bool, 1),
updated: make(chan struct{}, 1),
closing: make(chan struct{}),
}
timer.timeout <- timeout
go timer.run(ctx, cancel)

View File

@ -32,3 +32,15 @@ func TestActivityTimerUpdate(t *testing.T) {
assert(ctx.Err(), IsNotNil)
runtime.KeepAlive(timer)
}
func TestActivityTimerNonBlocking(t *testing.T) {
assert := With(t)
ctx, cancel := context.WithCancel(context.Background())
timer := CancelAfterInactivity(ctx, cancel, 0)
time.Sleep(time.Second * 1)
assert(ctx, HasDone)
timer.SetTimeout(0)
timer.SetTimeout(1)
timer.SetTimeout(2)
}

View File

@ -6,6 +6,7 @@ import (
"crypto/rand"
"encoding/hex"
"v2ray.com/core/common"
"v2ray.com/core/common/errors"
)
@ -46,11 +47,11 @@ func (u *UUID) Equals(another *UUID) bool {
}
// Next generates a deterministic random UUID based on this UUID.
func (u *UUID) Next() *UUID {
func (u *UUID) Next() UUID {
md5hash := md5.New()
md5hash.Write(u.Bytes())
md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81"))
newid := new(UUID)
var newid UUID
for {
md5hash.Sum(newid[:0])
if !newid.Equals(u) {
@ -61,30 +62,31 @@ func (u *UUID) Next() *UUID {
}
// New creates an UUID with random value.
func New() *UUID {
uuid := new(UUID)
rand.Read(uuid.Bytes())
func New() UUID {
var uuid UUID
common.Must2(rand.Read(uuid.Bytes()))
return uuid
}
// ParseBytes converts an UUID in byte form to object.
func ParseBytes(b []byte) (*UUID, error) {
func ParseBytes(b []byte) (UUID, error) {
var uuid UUID
if len(b) != 16 {
return nil, errors.New("invalid UUID: ", b)
return uuid, errors.New("invalid UUID: ", b)
}
uuid := new(UUID)
copy(uuid[:], b)
return uuid, nil
}
// ParseString converts an UUID in string form to object.
func ParseString(str string) (*UUID, error) {
func ParseString(str string) (UUID, error) {
var uuid UUID
text := []byte(str)
if len(text) < 32 {
return nil, errors.New("invalid UUID: ", str)
return uuid, errors.New("invalid UUID: ", str)
}
uuid := new(UUID)
b := uuid.Bytes()
for _, byteGroup := range byteGroups {
@ -95,7 +97,7 @@ func ParseString(str string) (*UUID, error) {
_, err := hex.Decode(b[:byteGroup/2], text[:byteGroup])
if err != nil {
return nil, err
return uuid, err
}
text = text[byteGroup:]

View File

@ -65,7 +65,9 @@ func TestEquals(t *testing.T) {
var uuid *UUID = nil
var uuid2 *UUID = nil
assert(uuid.Equals(uuid2), IsTrue)
assert(uuid.Equals(New()), IsFalse)
uuid3 := New()
assert(uuid.Equals(&uuid3), IsFalse)
}
func TestNext(t *testing.T) {
@ -73,5 +75,5 @@ func TestNext(t *testing.T) {
uuid := New()
uuid2 := uuid.Next()
assert(uuid.Equals(uuid2), IsFalse)
assert(uuid.Equals(&uuid2), IsFalse)
}

View File

@ -3,7 +3,6 @@ package core
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import v2ray_core_app_proxyman "v2ray.com/core/app/proxyman"
import v2ray_core_common_serial "v2ray.com/core/common/serial"
import v2ray_core_transport "v2ray.com/core/transport"
@ -40,12 +39,12 @@ func (x ConfigFormat) String() string {
}
func (ConfigFormat) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// Master config of V2Ray. V2Ray Core takes this config as input and functions accordingly.
// Master config of V2Ray. V2Ray takes this config as input and functions accordingly.
type Config struct {
// Inbound handler configurations. Must have at least one item.
Inbound []*v2ray_core_app_proxyman.InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"`
Inbound []*InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"`
// Outbound handler configurations. Must have at least one item. The first item is used as default for routing.
Outbound []*v2ray_core_app_proxyman.OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound" json:"outbound,omitempty"`
Outbound []*OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound" json:"outbound,omitempty"`
// App configuration. Must be one in the app directory.
App []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,4,rep,name=app" json:"app,omitempty"`
// Transport settings.
@ -60,14 +59,14 @@ func (m *Config) String() string { return proto.CompactTextString(m)
func (*Config) ProtoMessage() {}
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *Config) GetInbound() []*v2ray_core_app_proxyman.InboundHandlerConfig {
func (m *Config) GetInbound() []*InboundHandlerConfig {
if m != nil {
return m.Inbound
}
return nil
}
func (m *Config) GetOutbound() []*v2ray_core_app_proxyman.OutboundHandlerConfig {
func (m *Config) GetOutbound() []*OutboundHandlerConfig {
if m != nil {
return m.Outbound
}
@ -95,34 +94,131 @@ func (m *Config) GetExtension() []*v2ray_core_common_serial.TypedMessage {
return nil
}
type InboundHandlerConfig struct {
// Tag of the inbound handler.
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
// Settings for how this inbound proxy is handled. Must be ReceiverConfig above.
ReceiverSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings" json:"receiver_settings,omitempty"`
// Settings for inbound proxy. Must be one of the inbound proxies.
ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"`
}
func (m *InboundHandlerConfig) Reset() { *m = InboundHandlerConfig{} }
func (m *InboundHandlerConfig) String() string { return proto.CompactTextString(m) }
func (*InboundHandlerConfig) ProtoMessage() {}
func (*InboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *InboundHandlerConfig) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *InboundHandlerConfig) GetReceiverSettings() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.ReceiverSettings
}
return nil
}
func (m *InboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.ProxySettings
}
return nil
}
type OutboundHandlerConfig struct {
// Tag of this outbound handler.
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
// Settings for how to dial connection for this outbound handler. Must be SenderConfig above.
SenderSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=sender_settings,json=senderSettings" json:"sender_settings,omitempty"`
// Settings for this outbound proxy. Must be one of the outbound proxies.
ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"`
// If not zero, this outbound will be expired in seconds. Not used for now.
Expire int64 `protobuf:"varint,4,opt,name=expire" json:"expire,omitempty"`
// Comment of this outbound handler. Not used for now.
Comment string `protobuf:"bytes,5,opt,name=comment" json:"comment,omitempty"`
}
func (m *OutboundHandlerConfig) Reset() { *m = OutboundHandlerConfig{} }
func (m *OutboundHandlerConfig) String() string { return proto.CompactTextString(m) }
func (*OutboundHandlerConfig) ProtoMessage() {}
func (*OutboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *OutboundHandlerConfig) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *OutboundHandlerConfig) GetSenderSettings() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.SenderSettings
}
return nil
}
func (m *OutboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.TypedMessage {
if m != nil {
return m.ProxySettings
}
return nil
}
func (m *OutboundHandlerConfig) GetExpire() int64 {
if m != nil {
return m.Expire
}
return 0
}
func (m *OutboundHandlerConfig) GetComment() string {
if m != nil {
return m.Comment
}
return ""
}
func init() {
proto.RegisterType((*Config)(nil), "v2ray.core.Config")
proto.RegisterType((*InboundHandlerConfig)(nil), "v2ray.core.InboundHandlerConfig")
proto.RegisterType((*OutboundHandlerConfig)(nil), "v2ray.core.OutboundHandlerConfig")
proto.RegisterEnum("v2ray.core.ConfigFormat", ConfigFormat_name, ConfigFormat_value)
}
func init() { proto.RegisterFile("v2ray.com/core/config.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 336 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0xcd, 0x4a, 0xf3, 0x40,
0x18, 0x85, 0xbf, 0xb4, 0xf9, 0x6a, 0xfa, 0xb6, 0x48, 0x99, 0x55, 0xa8, 0x2e, 0x8a, 0xd0, 0x52,
0x04, 0x27, 0x12, 0x37, 0xe2, 0xd2, 0x8a, 0x3f, 0x05, 0x6d, 0xa9, 0xe2, 0xc2, 0x8d, 0x4c, 0xd3,
0x69, 0x09, 0x74, 0xe6, 0x1d, 0x26, 0x53, 0x69, 0x6e, 0xc9, 0x9b, 0xf2, 0x56, 0x24, 0x99, 0xfe,
0xa5, 0xe2, 0xc2, 0x55, 0x20, 0x73, 0x9e, 0xe7, 0x4c, 0x4e, 0xe0, 0xe8, 0x23, 0xd4, 0x2c, 0xa5,
0x11, 0x8a, 0x20, 0x42, 0xcd, 0x83, 0x08, 0xe5, 0x34, 0x9e, 0x51, 0xa5, 0xd1, 0x20, 0x81, 0xf5,
0xa1, 0xe6, 0xcd, 0xee, 0x5e, 0x90, 0x29, 0x15, 0x28, 0x8d, 0xcb, 0x54, 0x30, 0x59, 0xa0, 0x9a,
0xe7, 0x3f, 0x94, 0x42, 0xa0, 0x0c, 0x12, 0xae, 0x63, 0x36, 0x0f, 0x4c, 0xaa, 0xf8, 0xe4, 0x5d,
0xf0, 0x24, 0x61, 0x33, 0xbe, 0x22, 0xda, 0x7b, 0x84, 0xd1, 0x4c, 0x26, 0x0a, 0xb5, 0x29, 0x88,
0x4f, 0xbe, 0x4a, 0x50, 0xe9, 0xe5, 0x2f, 0xc8, 0x1d, 0x1c, 0xc4, 0x72, 0x8c, 0x0b, 0x39, 0xf1,
0x9d, 0x56, 0xb9, 0x5b, 0x0b, 0xcf, 0xe8, 0xf6, 0xae, 0x94, 0x29, 0x45, 0xd7, 0x77, 0xa3, 0x0f,
0x36, 0x77, 0xcf, 0xe4, 0x64, 0xce, 0xb5, 0xe5, 0x47, 0x6b, 0x9a, 0xf4, 0xc1, 0xc3, 0x85, 0xb1,
0xa6, 0x52, 0x6e, 0xa2, 0xbf, 0x9a, 0x06, 0xab, 0x60, 0x51, 0xb5, 0xe1, 0xc9, 0x25, 0x94, 0x99,
0x52, 0xbe, 0x9b, 0x6b, 0x3a, 0xbb, 0x1a, 0x3b, 0x01, 0xb5, 0x13, 0xd0, 0x97, 0x6c, 0x82, 0x47,
0xbb, 0xc0, 0x28, 0x43, 0xc8, 0x15, 0x54, 0x37, 0xdf, 0xec, 0xff, 0x6f, 0x39, 0xdd, 0x5a, 0x78,
0xbc, 0xcb, 0x6f, 0x0e, 0xe9, 0xaa, 0x74, 0x1b, 0x27, 0x37, 0x50, 0xe5, 0x4b, 0xc3, 0x65, 0x12,
0xa3, 0xf4, 0x2b, 0x7f, 0xea, 0xde, 0x82, 0x7d, 0xd7, 0x2b, 0x37, 0xdc, 0xd3, 0x0e, 0xd4, 0x6d,
0xc1, 0x2d, 0x6a, 0xc1, 0x0c, 0xa9, 0x83, 0x37, 0xcc, 0xa6, 0x1f, 0x2f, 0xa6, 0x8d, 0x7f, 0xc4,
0x03, 0xb7, 0xff, 0x3c, 0x78, 0x6a, 0x38, 0xd7, 0x6d, 0x38, 0x8c, 0x50, 0xec, 0xb4, 0x0c, 0x9d,
0x37, 0x37, 0x7b, 0x7e, 0x96, 0xe0, 0x35, 0x1c, 0xb1, 0x94, 0xf6, 0x50, 0xf3, 0x71, 0x25, 0xff,
0x6f, 0x17, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x62, 0x93, 0x6d, 0x78, 0x65, 0x02, 0x00, 0x00,
// 436 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x93, 0x41, 0x6f, 0xd3, 0x30,
0x14, 0xc7, 0x71, 0x13, 0xba, 0xf6, 0x6d, 0x94, 0x60, 0x01, 0xb2, 0x06, 0x87, 0x50, 0x69, 0x53,
0xc5, 0xc1, 0x45, 0xe5, 0x82, 0x26, 0x71, 0x61, 0x08, 0xc1, 0xa4, 0xd1, 0x29, 0x45, 0x1c, 0xb8,
0x4c, 0x6e, 0xfa, 0x56, 0x45, 0x5a, 0xec, 0xc8, 0x76, 0xa7, 0xe6, 0x2b, 0xf1, 0x3d, 0xb8, 0xf1,
0x8d, 0xb8, 0xa0, 0xc4, 0x49, 0x93, 0x41, 0x0f, 0x14, 0x69, 0xa7, 0xc4, 0x79, 0xfe, 0xfd, 0xdf,
0xfb, 0x25, 0x31, 0x3c, 0xbb, 0x99, 0x68, 0x91, 0xf3, 0x58, 0xa5, 0xe3, 0x58, 0x69, 0x1c, 0xc7,
0x4a, 0x5e, 0x25, 0x4b, 0x9e, 0x69, 0x65, 0x15, 0x85, 0xba, 0xa8, 0xf1, 0xf0, 0xd5, 0x5f, 0x1b,
0xd3, 0x54, 0xc9, 0xb1, 0x41, 0x9d, 0x88, 0xeb, 0xb1, 0xcd, 0x33, 0x5c, 0x5c, 0xa6, 0x68, 0x8c,
0x58, 0xa2, 0xa3, 0x0f, 0x8f, 0xfe, 0x20, 0xac, 0x16, 0xd2, 0x64, 0x4a, 0xdb, 0x5b, 0x4d, 0x86,
0x3f, 0x3a, 0xd0, 0x3d, 0x2d, 0x1f, 0xd0, 0x13, 0xd8, 0x4b, 0xe4, 0x5c, 0xad, 0xe4, 0x82, 0x91,
0xd0, 0x1b, 0xed, 0x4f, 0x42, 0xde, 0x4c, 0xc0, 0x3f, 0xb9, 0xd2, 0x47, 0x21, 0x17, 0xd7, 0xa8,
0x1d, 0x12, 0xd5, 0x00, 0x7d, 0x0b, 0x3d, 0xb5, 0xb2, 0x0e, 0xee, 0x94, 0xf0, 0x8b, 0x36, 0x3c,
0xad, 0x6a, 0xb7, 0xe9, 0x0d, 0x42, 0xdf, 0x80, 0x27, 0xb2, 0x8c, 0xf9, 0x25, 0x79, 0xdc, 0x26,
0x9d, 0x28, 0x77, 0xa2, 0xfc, 0x4b, 0x21, 0x7a, 0xee, 0x3c, 0xa3, 0x02, 0xa1, 0x27, 0xd0, 0xdf,
0x98, 0xb1, 0xfb, 0x21, 0x19, 0xed, 0x4f, 0x9e, 0xb7, 0xf9, 0x4d, 0x91, 0x57, 0x4d, 0x9b, 0xed,
0xf4, 0x3d, 0xf4, 0x71, 0x6d, 0x51, 0x9a, 0x44, 0x49, 0xd6, 0xdd, 0xa9, 0x77, 0x03, 0x9e, 0xf9,
0x3d, 0x2f, 0xf0, 0x87, 0x3f, 0x09, 0x3c, 0xde, 0xf6, 0x8a, 0x68, 0x00, 0x9e, 0x15, 0x4b, 0x46,
0x42, 0x32, 0xea, 0x47, 0xc5, 0x2d, 0x9d, 0xc1, 0x23, 0x8d, 0x31, 0x26, 0x37, 0xa8, 0x2f, 0x0d,
0x5a, 0x9b, 0xc8, 0xa5, 0x61, 0x9d, 0x72, 0xf4, 0x7f, 0x6d, 0x1f, 0xd4, 0x01, 0xb3, 0x8a, 0xa7,
0xe7, 0x30, 0xc8, 0xb4, 0x5a, 0xe7, 0x4d, 0xa2, 0xb7, 0x53, 0xe2, 0x83, 0x92, 0xae, 0xe3, 0x86,
0xbf, 0x08, 0x3c, 0xd9, 0xfa, 0xd1, 0xb6, 0xf8, 0x4c, 0xe1, 0xa1, 0x41, 0xb9, 0xf8, 0x7f, 0x9b,
0x81, 0xc3, 0xef, 0xc8, 0x85, 0x3e, 0x85, 0x2e, 0xae, 0xb3, 0x44, 0x23, 0xf3, 0x43, 0x32, 0xf2,
0xa2, 0x6a, 0x45, 0x19, 0xec, 0x15, 0x21, 0x28, 0xdd, 0x8f, 0xd3, 0x8f, 0xea, 0xe5, 0xcb, 0x63,
0x38, 0x70, 0xb6, 0x1f, 0x94, 0x4e, 0x85, 0xa5, 0x07, 0xd0, 0xbb, 0x28, 0x4e, 0xcb, 0x7c, 0x75,
0x15, 0xdc, 0xa3, 0x3d, 0xf0, 0xcf, 0x66, 0xd3, 0xcf, 0x01, 0x79, 0x77, 0x04, 0x83, 0x58, 0xa5,
0xad, 0xa9, 0x2e, 0xc8, 0x37, 0xbf, 0xb8, 0x7e, 0xef, 0xc0, 0xd7, 0x49, 0x24, 0x72, 0x7e, 0xaa,
0x34, 0xce, 0xbb, 0xe5, 0x51, 0x7b, 0xfd, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x59, 0x1b,
0xee, 0x03, 0x00, 0x00,
}

View File

@ -6,7 +6,6 @@ option go_package = "core";
option java_package = "com.v2ray.core";
option java_multiple_files = true;
import "v2ray.com/core/app/proxyman/config.proto";
import "v2ray.com/core/common/serial/typed_message.proto";
import "v2ray.com/core/transport/config.proto";
@ -19,10 +18,10 @@ enum ConfigFormat {
// Master config of V2Ray. V2Ray takes this config as input and functions accordingly.
message Config {
// Inbound handler configurations. Must have at least one item.
repeated v2ray.core.app.proxyman.InboundHandlerConfig inbound = 1;
repeated InboundHandlerConfig inbound = 1;
// Outbound handler configurations. Must have at least one item. The first item is used as default for routing.
repeated v2ray.core.app.proxyman.OutboundHandlerConfig outbound = 2;
repeated OutboundHandlerConfig outbound = 2;
reserved 3;
@ -36,3 +35,25 @@ message Config {
// V2Ray will ignore such config during initialization.
repeated v2ray.core.common.serial.TypedMessage extension = 6;
}
message InboundHandlerConfig {
// Tag of the inbound handler.
string tag = 1;
// Settings for how this inbound proxy is handled. Must be ReceiverConfig above.
v2ray.core.common.serial.TypedMessage receiver_settings = 2;
// Settings for inbound proxy. Must be one of the inbound proxies.
v2ray.core.common.serial.TypedMessage proxy_settings = 3;
}
message OutboundHandlerConfig {
// Tag of this outbound handler.
string tag = 1;
// Settings for how to dial connection for this outbound handler. Must be SenderConfig above.
v2ray.core.common.serial.TypedMessage sender_settings = 2;
// Settings for this outbound proxy. Must be one of the outbound proxies.
v2ray.core.common.serial.TypedMessage proxy_settings = 3;
// If not zero, this outbound will be expired in seconds. Not used for now.
int64 expire = 4;
// Comment of this outbound handler. Not used for now.
string comment = 5;
}

17
context.go Normal file
View File

@ -0,0 +1,17 @@
package core
import (
"context"
)
type key int
const v2rayKey key = 1
// FromContext returns a Instance from the given context, or nil if the context doesn't contain one.
func FromContext(ctx context.Context) *Instance {
if s, ok := ctx.Value(v2rayKey).(*Instance); ok {
return s
}
return nil
}

View File

@ -18,7 +18,7 @@ import (
)
var (
version = "3.6"
version = "3.8"
build = "Custom"
codename = "die Commanderin"
intro = "An unified platform for anti-censorship."

20
dial.go Normal file
View File

@ -0,0 +1,20 @@
package core
import (
"context"
"v2ray.com/core/common/net"
"v2ray.com/core/transport/ray"
)
// Dial provides an easy way for upstream caller to create net.Conn through V2Ray.
// It dispatches the request to the given destination by the given V2Ray instance.
// Since it is under a proxy context, the LocalAddr() and RemoteAddr() in returned net.Conn
// will not show real addresses being used for communication.
func Dial(ctx context.Context, v *Instance, dest net.Destination) (net.Conn, error) {
r, err := v.Dispatcher().Dispatch(ctx, dest)
if err != nil {
return nil, err
}
return ray.NewConnection(r.InboundOutput(), r.InboundInput()), nil
}

59
dns.go Normal file
View File

@ -0,0 +1,59 @@
package core
import (
"net"
"sync"
"v2ray.com/core/common"
)
// DNSClient is a V2Ray feature for querying DNS information.
type DNSClient interface {
Feature
LookupIP(host string) ([]net.IP, error)
}
type syncDNSClient struct {
sync.RWMutex
DNSClient
}
func (d *syncDNSClient) LookupIP(host string) ([]net.IP, error) {
d.RLock()
defer d.RUnlock()
if d.DNSClient == nil {
return net.LookupIP(host)
}
return d.DNSClient.LookupIP(host)
}
func (d *syncDNSClient) Start() error {
d.RLock()
defer d.RUnlock()
if d.DNSClient == nil {
return nil
}
return d.DNSClient.Start()
}
func (d *syncDNSClient) Close() error {
d.RLock()
defer d.RUnlock()
return common.Close(d.DNSClient)
}
func (d *syncDNSClient) Set(client DNSClient) {
if client == nil {
return
}
d.Lock()
defer d.Unlock()
d.DNSClient = client
}

View File

@ -2,10 +2,12 @@ package all
import (
// The following are necessary as they register handlers in their init functions.
_ "v2ray.com/core/app/dispatcher/impl"
_ "v2ray.com/core/app/commander"
_ "v2ray.com/core/app/dispatcher"
_ "v2ray.com/core/app/dns"
_ "v2ray.com/core/app/log"
_ "v2ray.com/core/app/policy/manager"
_ "v2ray.com/core/app/policy"
_ "v2ray.com/core/app/proxyman/command"
_ "v2ray.com/core/app/proxyman/inbound"
_ "v2ray.com/core/app/proxyman/outbound"
_ "v2ray.com/core/app/router"

168
network.go Normal file
View File

@ -0,0 +1,168 @@
package core
import (
"context"
"sync"
"v2ray.com/core/common"
"v2ray.com/core/common/net"
"v2ray.com/core/transport/ray"
)
// InboundHandler is the interface for handlers that process inbound connections.
type InboundHandler interface {
common.Runnable
// The tag of this handler.
Tag() string
// Deprecated. Do not use in new code.
GetRandomInboundProxy() (interface{}, net.Port, int)
}
// OutboundHandler is the interface for handlers that process outbound connections.
type OutboundHandler interface {
common.Runnable
Tag() string
Dispatch(ctx context.Context, outboundRay ray.OutboundRay)
}
// InboundHandlerManager is a feature that managers InboundHandlers.
type InboundHandlerManager interface {
Feature
// GetHandlers returns an InboundHandler for the given tag.
GetHandler(ctx context.Context, tag string) (InboundHandler, error)
// AddHandler adds the given handler into this InboundHandlerManager.
AddHandler(ctx context.Context, handler InboundHandler) error
// RemoveHandler removes a handler from InboundHandlerManager.
RemoveHandler(ctx context.Context, tag string) error
}
type syncInboundHandlerManager struct {
sync.RWMutex
InboundHandlerManager
}
func (m *syncInboundHandlerManager) GetHandler(ctx context.Context, tag string) (InboundHandler, error) {
m.RLock()
defer m.RUnlock()
if m.InboundHandlerManager == nil {
return nil, newError("InboundHandlerManager not set.").AtError()
}
return m.InboundHandlerManager.GetHandler(ctx, tag)
}
func (m *syncInboundHandlerManager) AddHandler(ctx context.Context, handler InboundHandler) error {
m.RLock()
defer m.RUnlock()
if m.InboundHandlerManager == nil {
return newError("InboundHandlerManager not set.").AtError()
}
return m.InboundHandlerManager.AddHandler(ctx, handler)
}
func (m *syncInboundHandlerManager) Start() error {
m.RLock()
defer m.RUnlock()
if m.InboundHandlerManager == nil {
return newError("InboundHandlerManager not set.").AtError()
}
return m.InboundHandlerManager.Start()
}
func (m *syncInboundHandlerManager) Close() error {
m.RLock()
defer m.RUnlock()
return common.Close(m.InboundHandlerManager)
}
func (m *syncInboundHandlerManager) Set(manager InboundHandlerManager) {
m.Lock()
defer m.Unlock()
m.InboundHandlerManager = manager
}
// OutboundHandlerManager is a feature that manages OutboundHandlers.
type OutboundHandlerManager interface {
Feature
// GetHandler returns an OutboundHandler will given tag.
GetHandler(tag string) OutboundHandler
// GetDefaultHandler returns the default OutboundHandler. It is usually the first OutboundHandler specified in the configuration.
GetDefaultHandler() OutboundHandler
// AddHandler adds a handler into this OutboundHandlerManager.
AddHandler(ctx context.Context, handler OutboundHandler) error
// RemoveHandler removes a handler from OutboundHandlerManager.
RemoveHandler(ctx context.Context, tag string) error
}
type syncOutboundHandlerManager struct {
sync.RWMutex
OutboundHandlerManager
}
func (m *syncOutboundHandlerManager) GetHandler(tag string) OutboundHandler {
m.RLock()
defer m.RUnlock()
if m.OutboundHandlerManager == nil {
return nil
}
return m.OutboundHandlerManager.GetHandler(tag)
}
func (m *syncOutboundHandlerManager) GetDefaultHandler() OutboundHandler {
m.RLock()
defer m.RUnlock()
if m.OutboundHandlerManager == nil {
return nil
}
return m.OutboundHandlerManager.GetDefaultHandler()
}
func (m *syncOutboundHandlerManager) AddHandler(ctx context.Context, handler OutboundHandler) error {
m.RLock()
defer m.RUnlock()
if m.OutboundHandlerManager == nil {
return newError("OutboundHandlerManager not set.").AtError()
}
return m.OutboundHandlerManager.AddHandler(ctx, handler)
}
func (m *syncOutboundHandlerManager) Start() error {
m.RLock()
defer m.RUnlock()
if m.OutboundHandlerManager == nil {
return newError("OutboundHandlerManager not set.").AtError()
}
return m.OutboundHandlerManager.Start()
}
func (m *syncOutboundHandlerManager) Close() error {
m.RLock()
defer m.RUnlock()
return common.Close(m.OutboundHandlerManager)
}
func (m *syncOutboundHandlerManager) Set(manager OutboundHandlerManager) {
m.Lock()
defer m.Unlock()
m.OutboundHandlerManager = manager
}

117
policy.go Normal file
View File

@ -0,0 +1,117 @@
package core
import (
"sync"
"time"
"v2ray.com/core/common"
)
// TimeoutPolicy contains limits for connection timeout.
type TimeoutPolicy struct {
// Timeout for handshake phase in a connection.
Handshake time.Duration
// Timeout for connection being idle, i.e., there is no egress or ingress traffic in this connection.
ConnectionIdle time.Duration
// Timeout for an uplink only connection, i.e., the downlink of the connection has ben closed.
UplinkOnly time.Duration
// Timeout for an downlink only connection, i.e., the uplink of the connection has ben closed.
DownlinkOnly time.Duration
}
// OverrideWith overrides the current TimeoutPolicy with another one. All timeouts with zero value will be overridden with the new value.
func (p TimeoutPolicy) OverrideWith(another TimeoutPolicy) TimeoutPolicy {
if p.Handshake == 0 {
p.Handshake = another.Handshake
}
if p.ConnectionIdle == 0 {
p.ConnectionIdle = another.ConnectionIdle
}
if p.UplinkOnly == 0 {
p.UplinkOnly = another.UplinkOnly
}
if p.DownlinkOnly == 0 {
p.DownlinkOnly = another.DownlinkOnly
}
return p
}
// Policy is session based settings for controlling V2Ray requests. It contains various settings (or limits) that may differ for different users in the context.
type Policy struct {
Timeouts TimeoutPolicy // Timeout settings
}
// OverrideWith overrides the current Policy with another one. All values with default value will be overridden.
func (p Policy) OverrideWith(another Policy) Policy {
p.Timeouts.OverrideWith(another.Timeouts)
return p
}
// PolicyManager is a feature that provides Policy for the given user by its id or level.
type PolicyManager interface {
Feature
// ForLevel returns the Policy for the given user level.
ForLevel(level uint32) Policy
}
// DefaultPolicy returns the Policy when user is not specified.
func DefaultPolicy() Policy {
return Policy{
Timeouts: TimeoutPolicy{
Handshake: time.Second * 4,
ConnectionIdle: time.Second * 300,
UplinkOnly: time.Second * 5,
DownlinkOnly: time.Second * 30,
},
}
}
type syncPolicyManager struct {
sync.RWMutex
PolicyManager
}
func (m *syncPolicyManager) ForLevel(level uint32) Policy {
m.RLock()
defer m.RUnlock()
if m.PolicyManager == nil {
p := DefaultPolicy()
if level == 1 {
p.Timeouts.ConnectionIdle = time.Second * 600
}
return p
}
return m.PolicyManager.ForLevel(level)
}
func (m *syncPolicyManager) Start() error {
m.RLock()
defer m.RUnlock()
if m.PolicyManager == nil {
return nil
}
return m.PolicyManager.Start()
}
func (m *syncPolicyManager) Close() error {
m.RLock()
defer m.RUnlock()
return common.Close(m.PolicyManager)
}
func (m *syncPolicyManager) Set(manager PolicyManager) {
if manager == nil {
return
}
m.Lock()
defer m.Unlock()
m.PolicyManager = manager
}

View File

@ -4,10 +4,9 @@ package dokodemo
import (
"context"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core/app/policy"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
@ -18,36 +17,29 @@ import (
)
type DokodemoDoor struct {
config *Config
address net.Address
port net.Port
policy policy.Policy
policyManager core.PolicyManager
config *Config
address net.Address
port net.Port
v *core.Instance
}
func New(ctx context.Context, config *Config) (*DokodemoDoor, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context")
}
if config.NetworkList == nil || config.NetworkList.Size() == 0 {
return nil, newError("no network specified")
}
d := &DokodemoDoor{
config: config,
address: config.GetPredefinedAddress(),
port: net.Port(config.Port),
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
space.On(app.SpaceInitializing, func(interface{}) error {
pm := policy.FromSpace(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
})
d := &DokodemoDoor{
config: config,
address: config.GetPredefinedAddress(),
port: net.Port(config.Port),
policyManager: v.PolicyManager(),
}
return d, nil
}
@ -55,7 +47,16 @@ func (d *DokodemoDoor) Network() net.NetworkList {
return *(d.config.NetworkList)
}
func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error {
func (d *DokodemoDoor) policy() core.Policy {
config := d.config
p := d.policyManager.ForLevel(config.UserLevel)
if config.Timeout > 0 && config.UserLevel == 0 {
p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second
}
return p
}
func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error {
newError("processing connection from: ", conn.RemoteAddr()).AtDebug().WriteToLog()
dest := net.Destination{
Network: network,
@ -72,7 +73,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
}
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, d.policy.Timeout.ConnectionIdle.Duration())
timer := signal.CancelAfterInactivity(ctx, cancel, d.policy().Timeouts.ConnectionIdle)
inboundRay, err := dispatcher.Dispatch(ctx, dest)
if err != nil {
@ -88,7 +89,7 @@ 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())
timer.SetTimeout(d.policy().Timeouts.DownlinkOnly)
return nil
})
@ -115,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(d.policy.Timeout.UplinkOnly.Duration())
timer.SetTimeout(d.policy().Timeouts.UplinkOnly)
return nil
})

View File

@ -4,9 +4,9 @@ package freedom
import (
"context"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/policy"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/dice"
@ -20,37 +20,35 @@ import (
// Handler handles Freedom connections.
type Handler struct {
domainStrategy Config_DomainStrategy
timeout uint32
destOverride *DestinationOverride
policy policy.Policy
policyManager core.PolicyManager
dns core.DNSClient
config Config
}
// New creates a new Freedom handler.
func New(ctx context.Context, config *Config) (*Handler, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context")
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not found in context.")
}
f := &Handler{
domainStrategy: config.DomainStrategy,
timeout: config.Timeout,
destOverride: config.DestinationOverride,
config: *config,
policyManager: v.PolicyManager(),
dns: v.DNSClient(),
}
space.On(app.SpaceInitializing, func(interface{}) error {
pm := policy.FromSpace(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
}
func (h *Handler) policy() core.Policy {
p := h.policyManager.ForLevel(h.config.UserLevel)
if h.config.Timeout > 0 && h.config.UserLevel == 0 {
p.Timeouts.ConnectionIdle = time.Duration(h.config.Timeout) * time.Second
}
return p
}
func (h *Handler) resolveIP(ctx context.Context, domain string) net.Address {
if resolver, ok := proxy.ResolvedIPsFromContext(ctx); ok {
ips := resolver.Resolve()
@ -60,7 +58,7 @@ func (h *Handler) resolveIP(ctx context.Context, domain string) net.Address {
return ips[dice.Roll(len(ips))]
}
ips, err := net.LookupIP(domain)
ips, err := h.dns.LookupIP(domain)
if err != nil {
newError("failed to get IP address for domain ", domain).Base(err).WriteToLog()
}
@ -73,8 +71,8 @@ func (h *Handler) resolveIP(ctx context.Context, domain string) net.Address {
// Process implements proxy.Outbound.
func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error {
destination, _ := proxy.TargetFromContext(ctx)
if h.destOverride != nil {
server := h.destOverride.Server
if h.config.DestinationOverride != nil {
server := h.config.DestinationOverride.Server
destination = net.Destination{
Network: destination.Network,
Address: server.Address.AsAddress(),
@ -86,7 +84,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
input := outboundRay.OutboundInput()
output := outboundRay.OutboundOutput()
if h.domainStrategy == Config_USE_IP && destination.Address.Family().IsDomain() {
if h.config.DomainStrategy == Config_USE_IP && destination.Address.Family().IsDomain() {
ip := h.resolveIP(ctx, destination.Address.Domain())
if ip != nil {
destination = net.Destination{
@ -113,7 +111,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
defer conn.Close()
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, h.policy.Timeout.ConnectionIdle.Duration())
timer := signal.CancelAfterInactivity(ctx, cancel, h.policy().Timeouts.ConnectionIdle)
requestDone := signal.ExecuteAsync(func() error {
var writer buf.Writer
@ -125,7 +123,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())
timer.SetTimeout(h.policy().Timeouts.DownlinkOnly)
return nil
})
@ -136,7 +134,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())
timer.SetTimeout(h.policy().Timeouts.UplinkOnly)
return nil
})

View File

@ -1,33 +0,0 @@
package proxy
import (
"context"
"v2ray.com/core/common"
)
func CreateInboundHandler(ctx context.Context, config interface{}) (Inbound, error) {
handler, err := common.CreateObject(ctx, config)
if err != nil {
return nil, newError("failed to create inbound handler").Base(err)
}
switch h := handler.(type) {
case Inbound:
return h, nil
default:
return nil, newError("not a InboundHandler")
}
}
func CreateOutboundHandler(ctx context.Context, config interface{}) (Outbound, error) {
handler, err := common.CreateObject(ctx, config)
if err != nil {
return nil, newError("failed to create outbound handler").Base(err)
}
switch h := handler.(type) {
case Outbound:
return h, nil
default:
return nil, newError("not a OutboundHandler")
}
}

View File

@ -10,9 +10,7 @@ import (
"strings"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core/app/policy"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/errors"
@ -26,32 +24,31 @@ import (
// Server is a HTTP proxy server.
type Server struct {
config *ServerConfig
policy policy.Policy
v *core.Instance
}
// NewServer creates a new HTTP inbound handler.
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context.")
}
s := &Server{
config: config,
v: core.FromContext(ctx),
}
space.On(app.SpaceInitializing, func(interface{}) error {
pm := policy.FromSpace(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
})
if s.v == nil {
return nil, newError("V is not in context.")
}
return s, nil
}
func (s *Server) policy() core.Policy {
config := s.config
p := s.v.PolicyManager().ForLevel(config.UserLevel)
if config.Timeout > 0 && config.UserLevel == 0 {
p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second
}
return p
}
func (*Server) Network() net.NetworkList {
return net.NetworkList{
Network: []net.Network{net.Network_TCP},
@ -104,11 +101,11 @@ type readerOnly struct {
io.Reader
}
func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error {
func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error {
reader := bufio.NewReaderSize(readerOnly{conn}, buf.Size)
Start:
conn.SetReadDeadline(time.Now().Add(s.policy.Timeout.Handshake.Duration()))
conn.SetReadDeadline(time.Now().Add(s.policy().Timeouts.Handshake))
request, err := http.ReadRequest(reader)
if err != nil {
@ -165,14 +162,14 @@ Start:
return err
}
func (s *Server) handleConnect(ctx context.Context, request *http.Request, reader *bufio.Reader, conn internet.Connection, dest net.Destination, dispatcher dispatcher.Interface) error {
func (s *Server) handleConnect(ctx context.Context, request *http.Request, reader *bufio.Reader, conn internet.Connection, dest net.Destination, dispatcher core.Dispatcher) error {
_, err := conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
if err != nil {
return newError("failed to write back OK response").Base(err)
}
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, s.policy.Timeout.ConnectionIdle.Duration())
timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle)
ray, err := dispatcher.Dispatch(ctx, dest)
if err != nil {
return err
@ -191,7 +188,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())
defer timer.SetTimeout(s.policy().Timeouts.DownlinkOnly)
v2reader := buf.NewReader(conn)
return buf.Copy(v2reader, ray.InboundInput(), buf.UpdateActivity(timer))
@ -202,7 +199,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(s.policy.Timeout.UplinkOnly.Duration())
timer.SetTimeout(s.policy().Timeouts.UplinkOnly)
return nil
})
@ -217,7 +214,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
var errWaitAnother = newError("keep alive")
func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, writer io.Writer, dest net.Destination, dispatcher dispatcher.Interface) error {
func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, writer io.Writer, dest net.Destination, dispatcher core.Dispatcher) error {
if !s.config.AllowTransparent && len(request.URL.Host) <= 0 {
// RFC 2068 (HTTP/1.1) requires URL to be absolute URL in HTTP proxy.
response := &http.Response{

View File

@ -10,8 +10,9 @@ package proxy
import (
"context"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray"
)
@ -22,7 +23,7 @@ type Inbound interface {
Network() net.NetworkList
// Process processes a connection of given network. If necessary, the Inbound can dispatch the connection to an Outbound.
Process(context.Context, net.Network, internet.Connection, dispatcher.Interface) error
Process(context.Context, net.Network, internet.Connection, core.Dispatcher) error
}
// An Outbound process outbound connections.
@ -36,3 +37,20 @@ type Dialer interface {
// Dial dials a system connection to the given destination.
Dial(ctx context.Context, destination net.Destination) (internet.Connection, error)
}
// UserManager is the interface for Inbounds and Outbounds that can manage their users.
type UserManager interface {
// AddUser adds a new user.
AddUser(context.Context, *protocol.User) error
// RemoveUser removes an user by email.
RemoveUser(context.Context, string) error
}
type GetInbound interface {
GetInbound() Inbound
}
type GetOutbound interface {
GetOutbound() Outbound
}

View File

@ -3,8 +3,7 @@ package shadowsocks
import (
"context"
"v2ray.com/core/app"
"v2ray.com/core/app/policy"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
@ -18,8 +17,8 @@ import (
// Client is a inbound handler for Shadowsocks protocol
type Client struct {
serverPicker protocol.ServerPicker
policyManager policy.Manager
serverPicker protocol.ServerPicker
v *core.Instance
}
// NewClient create a new Shadowsocks client.
@ -33,19 +32,11 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
}
client := &Client{
serverPicker: protocol.NewRoundRobinServerPicker(serverList),
v: core.FromContext(ctx),
}
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("Space not found.")
if client.v == nil {
return nil, newError("V is not in context.")
}
space.On(app.SpaceInitializing, func(interface{}) error {
pm := policy.FromSpace(space)
if pm == nil {
return newError("Policy not found in space.")
}
client.policyManager = pm
return nil
})
return client, nil
}
@ -103,9 +94,9 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
request.Option |= RequestOptionOneTimeAuth
}
sessionPolicy := v.policyManager.GetPolicy(user.Level)
sessionPolicy := v.v.PolicyManager().ForLevel(user.Level)
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration())
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
if request.Command == protocol.RequestCommandTCP {
bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
@ -119,13 +110,13 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
}
requestDone := signal.ExecuteAsync(func() error {
defer timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration())
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
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())
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
responseReader, err := ReadTCPResponse(user, conn)
if err != nil {
@ -164,7 +155,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
User: user,
}
if err := buf.Copy(reader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer)); err != nil {
if err := buf.Copy(reader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer), buf.IgnoreReaderError()); err != nil {
return newError("failed to transport all UDP response").Base(err)
}
return nil

View File

@ -140,6 +140,9 @@ func (v *AesCfb) EncodePacket(key []byte, b *buf.Buffer) error {
}
func (v *AesCfb) DecodePacket(key []byte, b *buf.Buffer) error {
if b.Len() <= v.IVSize() {
return newError("insufficient data: ", b.Len())
}
iv := b.BytesTo(v.IVSize())
stream := crypto.NewAesDecryptionStream(key, iv)
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))
@ -203,6 +206,9 @@ func (c *AEADCipher) EncodePacket(key []byte, b *buf.Buffer) error {
}
func (c *AEADCipher) DecodePacket(key []byte, b *buf.Buffer) error {
if b.Len() <= c.IVSize() {
return newError("insufficient data: ", b.Len())
}
ivLen := c.IVSize()
payloadLen := b.Len()
auth := c.createAuthenticator(key, b.BytesTo(ivLen))
@ -253,6 +259,9 @@ func (v *ChaCha20) EncodePacket(key []byte, b *buf.Buffer) error {
}
func (v *ChaCha20) DecodePacket(key []byte, b *buf.Buffer) error {
if b.Len() <= v.IVSize() {
return newError("insufficient data: ", b.Len())
}
iv := b.BytesTo(v.IVSize())
stream := crypto.NewChaCha20Stream(key, iv)
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))

View File

@ -4,9 +4,7 @@ import (
"context"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core/app/policy"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/log"
@ -19,18 +17,14 @@ import (
)
type Server struct {
config *ServerConfig
user *protocol.User
account *MemoryAccount
policyManager policy.Manager
config *ServerConfig
user *protocol.User
account *MemoryAccount
v *core.Instance
}
// NewServer create a new Shadowsocks server.
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context")
}
if config.GetUser() == nil {
return nil, newError("user is not specified")
}
@ -45,16 +39,12 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
config: config,
user: config.GetUser(),
account: account,
v: core.FromContext(ctx),
}
space.On(app.SpaceInitializing, func(interface{}) error {
pm := policy.FromSpace(space)
if pm == nil {
return newError("Policy not found in space.")
}
s.policyManager = pm
return nil
})
if s.v == nil {
return nil, newError("V is not in context.")
}
return s, nil
}
@ -69,7 +59,7 @@ func (s *Server) Network() net.NetworkList {
return list
}
func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error {
func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error {
switch network {
case net.Network_TCP:
return s.handleConnection(ctx, conn, dispatcher)
@ -80,7 +70,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet
}
}
func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error {
udpServer := udp.NewDispatcher(dispatcher)
reader := buf.NewReader(conn)
@ -148,9 +138,9 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
return nil
}
func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
sessionPolicy := s.policyManager.GetPolicy(s.user.Level)
conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeout.Handshake.Duration()))
func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error {
sessionPolicy := s.v.PolicyManager().ForLevel(s.user.Level)
conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake))
bufferedReader := buf.NewBufferedReader(buf.NewReader(conn))
request, bodyReader, err := ReadTCPSession(s.user, bufferedReader)
if err != nil {
@ -178,7 +168,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
ctx = protocol.ContextWithUser(ctx, request.User)
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration())
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
ray, err := dispatcher.Dispatch(ctx, dest)
if err != nil {
return err
@ -208,7 +198,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
return newError("failed to transport all TCP response").Base(err)
}
timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration())
timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
return nil
})
@ -219,7 +209,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())
timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
return nil
})

View File

@ -5,9 +5,7 @@ import (
"io"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core/app/policy"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/log"
@ -22,32 +20,30 @@ import (
// Server is a SOCKS 5 proxy server
type Server struct {
config *ServerConfig
policy policy.Policy
v *core.Instance
}
// NewServer creates a new Server object.
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context").AtWarning()
}
s := &Server{
config: config,
v: core.FromContext(ctx),
}
if s.v == nil {
return nil, newError("V is not in context.")
}
space.On(app.SpaceInitializing, func(interface{}) error {
pm := policy.FromSpace(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
}
func (s *Server) policy() core.Policy {
config := s.config
p := s.v.PolicyManager().ForLevel(config.UserLevel)
if config.Timeout > 0 && config.UserLevel == 0 {
p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second
}
return p
}
func (s *Server) Network() net.NetworkList {
list := net.NetworkList{
Network: []net.Network{net.Network_TCP},
@ -58,7 +54,7 @@ func (s *Server) Network() net.NetworkList {
return list
}
func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher dispatcher.Interface) error {
func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error {
switch network {
case net.Network_TCP:
return s.processTCP(ctx, conn, dispatcher)
@ -69,8 +65,8 @@ 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(s.policy.Timeout.Handshake.Duration()))
func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error {
conn.SetReadDeadline(time.Now().Add(s.policy().Timeouts.Handshake))
reader := buf.NewBufferedReader(buf.NewReader(conn))
inboundDest, ok := proxy.InboundEntryPointFromContext(ctx)
@ -125,9 +121,9 @@ func (*Server) handleUDP(c net.Conn) error {
return err
}
func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher dispatcher.Interface) error {
func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher core.Dispatcher) error {
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, v.policy.Timeout.ConnectionIdle.Duration())
timer := signal.CancelAfterInactivity(ctx, cancel, v.policy().Timeouts.ConnectionIdle)
ray, err := dispatcher.Dispatch(ctx, dest)
if err != nil {
@ -144,7 +140,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())
timer.SetTimeout(v.policy().Timeouts.DownlinkOnly)
return nil
})
@ -153,7 +149,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(v.policy.Timeout.UplinkOnly.Duration())
timer.SetTimeout(v.policy().Timeouts.UplinkOnly)
return nil
})
@ -166,7 +162,7 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
return nil
}
func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher dispatcher.Interface) error {
func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error {
udpServer := udp.NewDispatcher(dispatcher)
if source, ok := proxy.SourceFromContext(ctx); ok {

View File

@ -31,8 +31,7 @@ func (a *InternalAccount) Equals(account protocol.Account) bool {
func (a *Account) AsAccount() (protocol.Account, error) {
id, err := uuid.ParseString(a.Id)
if err != nil {
newError("failed to parse ID").Base(err).AtError().WriteToLog()
return nil, err
return nil, newError("failed to parse ID").Base(err).AtError()
}
protoID := protocol.NewID(id)
return &InternalAccount{

View File

@ -131,7 +131,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
}
if request.Security.Is(protocol.SecurityType_NONE) {
if request.Option.Has(protocol.RequestOptionChunkStream) {
if request.Command == protocol.RequestCommandTCP {
if request.Command.TransferType() == protocol.TransferTypeStream {
return crypto.NewChunkStreamWriter(sizeParser, writer)
}
auth := &crypto.AEADAuthenticator{
@ -236,7 +236,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
}
if request.Security.Is(protocol.SecurityType_NONE) {
if request.Option.Has(protocol.RequestOptionChunkStream) {
if request.Command == protocol.RequestCommandTCP {
if request.Command.TransferType() == protocol.TransferTypeStream {
return crypto.NewChunkStreamReader(sizeParser, reader)
}

View File

@ -1,7 +1,6 @@
package encoding_test
import (
"context"
"testing"
"v2ray.com/core/common"
@ -22,8 +21,9 @@ func TestRequestSerialization(t *testing.T) {
Level: 0,
Email: "test@v2ray.com",
}
id := uuid.New()
account := &vmess.Account{
Id: uuid.New().String(),
Id: id.String(),
AlterId: 0,
}
user.Account = serial.ToTypedMessage(account)
@ -44,11 +44,12 @@ func TestRequestSerialization(t *testing.T) {
buffer2 := buf.New()
buffer2.Append(buffer.Bytes())
ctx, cancel := context.WithCancel(context.Background())
sessionHistory := NewSessionHistory(ctx)
sessionHistory := NewSessionHistory()
defer common.Close(sessionHistory)
userValidator := vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash)
userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash)
userValidator.Add(user)
defer common.Close(userValidator)
server := NewServerSession(userValidator, sessionHistory)
actualRequest, err := server.DecodeRequestHeader(buffer)
@ -64,6 +65,91 @@ func TestRequestSerialization(t *testing.T) {
_, err = server.DecodeRequestHeader(buffer2)
// anti replay attack
assert(err, IsNotNil)
cancel()
}
func TestInvalidRequest(t *testing.T) {
assert := With(t)
user := &protocol.User{
Level: 0,
Email: "test@v2ray.com",
}
id := uuid.New()
account := &vmess.Account{
Id: id.String(),
AlterId: 0,
}
user.Account = serial.ToTypedMessage(account)
expectedRequest := &protocol.RequestHeader{
Version: 1,
User: user,
Command: protocol.RequestCommand(100),
Address: net.DomainAddress("www.v2ray.com"),
Port: net.Port(443),
Security: protocol.Security(protocol.SecurityType_AES128_GCM),
}
buffer := buf.New()
client := NewClientSession(protocol.DefaultIDHash)
common.Must(client.EncodeRequestHeader(expectedRequest, buffer))
buffer2 := buf.New()
buffer2.Append(buffer.Bytes())
sessionHistory := NewSessionHistory()
defer common.Close(sessionHistory)
userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash)
userValidator.Add(user)
defer common.Close(userValidator)
server := NewServerSession(userValidator, sessionHistory)
_, err := server.DecodeRequestHeader(buffer)
assert(err, IsNotNil)
}
func TestMuxRequest(t *testing.T) {
assert := With(t)
user := &protocol.User{
Level: 0,
Email: "test@v2ray.com",
}
id := uuid.New()
account := &vmess.Account{
Id: id.String(),
AlterId: 0,
}
user.Account = serial.ToTypedMessage(account)
expectedRequest := &protocol.RequestHeader{
Version: 1,
User: user,
Command: protocol.RequestCommandMux,
Security: protocol.Security(protocol.SecurityType_AES128_GCM),
}
buffer := buf.New()
client := NewClientSession(protocol.DefaultIDHash)
common.Must(client.EncodeRequestHeader(expectedRequest, buffer))
buffer2 := buf.New()
buffer2.Append(buffer.Bytes())
sessionHistory := NewSessionHistory()
defer common.Close(sessionHistory)
userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash)
userValidator.Add(user)
defer common.Close(userValidator)
server := NewServerSession(userValidator, sessionHistory)
actualRequest, err := server.DecodeRequestHeader(buffer)
assert(err, IsNil)
assert(expectedRequest.Version, Equals, actualRequest.Version)
assert(byte(expectedRequest.Command), Equals, byte(actualRequest.Command))
assert(byte(expectedRequest.Option), Equals, byte(actualRequest.Option))
assert(byte(expectedRequest.Security), Equals, byte(actualRequest.Security))
}

View File

@ -1,7 +1,6 @@
package encoding
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
@ -10,6 +9,8 @@ import (
"sync"
"time"
"v2ray.com/core/common/dice"
"golang.org/x/crypto/chacha20poly1305"
"v2ray.com/core/common"
"v2ray.com/core/common/bitmask"
@ -31,29 +32,34 @@ type sessionId struct {
type SessionHistory struct {
sync.RWMutex
cache map[sessionId]time.Time
token *signal.Semaphore
ctx context.Context
task *signal.PeriodicTask
}
func NewSessionHistory(ctx context.Context) *SessionHistory {
func NewSessionHistory() *SessionHistory {
h := &SessionHistory{
cache: make(map[sessionId]time.Time, 128),
token: signal.NewSemaphore(1),
ctx: ctx,
}
h.task = &signal.PeriodicTask{
Interval: time.Second * 30,
Execute: func() error {
h.removeExpiredEntries()
return nil
},
}
common.Must(h.task.Start())
return h
}
// Close implements common.Closable.
func (h *SessionHistory) Close() error {
return h.task.Close()
}
func (h *SessionHistory) add(session sessionId) {
h.Lock()
h.cache[session] = time.Now().Add(time.Minute * 3)
h.Unlock()
defer h.Unlock()
select {
case <-h.token.Wait():
go h.run()
default:
}
h.cache[session] = time.Now().Add(time.Minute * 3)
}
func (h *SessionHistory) has(session sessionId) bool {
@ -66,31 +72,16 @@ func (h *SessionHistory) has(session sessionId) bool {
return false
}
func (h *SessionHistory) run() {
defer h.token.Signal()
func (h *SessionHistory) removeExpiredEntries() {
now := time.Now()
for {
select {
case <-h.ctx.Done():
return
case <-time.After(time.Second * 30):
}
session2Remove := make([]sessionId, 0, 16)
now := time.Now()
h.Lock()
if len(h.cache) == 0 {
h.Unlock()
return
}
for session, expire := range h.cache {
if expire.Before(now) {
session2Remove = append(session2Remove, session)
}
}
for _, session := range session2Remove {
h.Lock()
defer h.Unlock()
for session, expire := range h.cache {
if expire.Before(now) {
delete(h.cache, session)
}
h.Unlock()
}
}
@ -114,6 +105,44 @@ func NewServerSession(validator protocol.UserValidator, sessionHistory *SessionH
}
}
func readAddress(buffer *buf.Buffer, reader io.Reader) (net.Address, net.Port, error) {
var address net.Address
var port net.Port
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 3)); err != nil {
return address, port, newError("failed to read port and address type").Base(err)
}
port = net.PortFromBytes(buffer.BytesRange(-3, -1))
addressType := protocol.AddressType(buffer.Byte(buffer.Len() - 1))
switch addressType {
case protocol.AddressTypeIPv4:
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil {
return address, port, newError("failed to read IPv4 address").Base(err)
}
address = net.IPAddress(buffer.BytesFrom(-4))
case protocol.AddressTypeIPv6:
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil {
return address, port, newError("failed to read IPv6 address").Base(err)
}
address = net.IPAddress(buffer.BytesFrom(-16))
case protocol.AddressTypeDomain:
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil {
return address, port, newError("failed to read domain address").Base(err)
}
domainLength := int(buffer.Byte(buffer.Len() - 1))
if domainLength == 0 {
return address, port, newError("zero length domain")
}
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil {
return address, port, newError("failed to read domain address").Base(err)
}
address = net.DomainAddress(string(buffer.BytesFrom(-domainLength)))
default:
return address, port, newError("invalid address type", addressType)
}
return address, port, nil
}
func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.RequestHeader, error) {
buffer := buf.New()
defer buffer.Release()
@ -139,7 +168,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv)
decryptor := crypto.NewCryptionReader(aesStream, reader)
if err := buffer.Reset(buf.ReadFullFrom(decryptor, 41)); err != nil {
if err := buffer.Reset(buf.ReadFullFrom(decryptor, 38)); err != nil {
return nil, newError("failed to read request header").Base(err)
}
@ -148,10 +177,6 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
Version: buffer.Byte(0),
}
if request.Version != Version {
return nil, newError("invalid protocol version ", request.Version)
}
s.requestBodyIV = append([]byte(nil), buffer.BytesRange(1, 17)...) // 16 bytes
s.requestBodyKey = append([]byte(nil), buffer.BytesRange(17, 33)...) // 16 bytes
var sid sessionId
@ -170,33 +195,28 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
// 1 bytes reserved
request.Command = protocol.RequestCommand(buffer.Byte(37))
if request.Command != protocol.RequestCommandMux {
request.Port = net.PortFromBytes(buffer.BytesRange(38, 40))
switch protocol.AddressType(buffer.Byte(40)) {
case protocol.AddressTypeIPv4:
if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, 4)); err != nil {
return nil, newError("failed to read IPv4 address").Base(err)
}
request.Address = net.IPAddress(buffer.BytesFrom(-4))
case protocol.AddressTypeIPv6:
if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, 16)); err != nil {
return nil, newError("failed to read IPv6 address").Base(err)
}
request.Address = net.IPAddress(buffer.BytesFrom(-16))
case protocol.AddressTypeDomain:
if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, 1)); err != nil {
return nil, newError("failed to read domain address").Base(err)
}
domainLength := int(buffer.Byte(buffer.Len() - 1))
if domainLength == 0 {
return nil, newError("zero length domain").Base(err)
}
if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, domainLength)); err != nil {
return nil, newError("failed to read domain address").Base(err)
}
request.Address = net.DomainAddress(string(buffer.BytesFrom(-domainLength)))
invalidRequest := false
switch request.Command {
case protocol.RequestCommandMux:
request.Address = net.DomainAddress("v1.mux.cool")
request.Port = 0
case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
if addr, port, err := readAddress(buffer, decryptor); err == nil {
request.Address = addr
request.Port = port
} else {
invalidRequest = true
newError("failed to read address").Base(err).WriteToLog()
}
default:
invalidRequest = true
}
if invalidRequest {
randomLen := dice.Roll(32) + 1
// Read random number of bytes for prevent detection.
buffer.AppendSupplier(buf.ReadFullFrom(decryptor, randomLen))
return nil, newError("invalid request")
}
if padingLen > 0 {
@ -232,7 +252,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
}
if request.Security.Is(protocol.SecurityType_NONE) {
if request.Option.Has(protocol.RequestOptionChunkStream) {
if request.Command == protocol.RequestCommandTCP {
if request.Command.TransferType() == protocol.TransferTypeStream {
return crypto.NewChunkStreamReader(sizeParser, reader)
}
@ -318,7 +338,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
}
if request.Security.Is(protocol.SecurityType_NONE) {
if request.Option.Has(protocol.RequestOptionChunkStream) {
if request.Command == protocol.RequestCommandTCP {
if request.Command.TransferType() == protocol.TransferTypeStream {
return crypto.NewChunkStreamWriter(sizeParser, writer)
}

View File

@ -5,13 +5,11 @@ package inbound
import (
"context"
"io"
"strings"
"sync"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core/app/policy"
"v2ray.com/core/app/proxyman"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/errors"
@ -28,96 +26,116 @@ import (
)
type userByEmail struct {
sync.RWMutex
sync.Mutex
cache map[string]*protocol.User
defaultLevel uint32
defaultAlterIDs uint16
}
func newUserByEmail(users []*protocol.User, config *DefaultConfig) *userByEmail {
cache := make(map[string]*protocol.User)
for _, user := range users {
cache[user.Email] = user
}
func newUserByEmail(config *DefaultConfig) *userByEmail {
return &userByEmail{
cache: cache,
cache: make(map[string]*protocol.User),
defaultLevel: config.Level,
defaultAlterIDs: uint16(config.AlterId),
}
}
func (v *userByEmail) addNoLock(u *protocol.User) bool {
email := strings.ToLower(u.Email)
user, found := v.cache[email]
if found {
return false
}
v.cache[email] = user
return true
}
func (v *userByEmail) Add(u *protocol.User) bool {
v.Lock()
defer v.Unlock()
return v.addNoLock(u)
}
func (v *userByEmail) Get(email string) (*protocol.User, bool) {
var user *protocol.User
var found bool
v.RLock()
user, found = v.cache[email]
v.RUnlock()
email = strings.ToLower(email)
v.Lock()
defer v.Unlock()
user, found := v.cache[email]
if !found {
v.Lock()
user, found = v.cache[email]
if !found {
account := &vmess.Account{
Id: uuid.New().String(),
AlterId: uint32(v.defaultAlterIDs),
}
user = &protocol.User{
Level: v.defaultLevel,
Email: email,
Account: serial.ToTypedMessage(account),
}
v.cache[email] = user
id := uuid.New()
account := &vmess.Account{
Id: id.String(),
AlterId: uint32(v.defaultAlterIDs),
}
v.Unlock()
user = &protocol.User{
Level: v.defaultLevel,
Email: email,
Account: serial.ToTypedMessage(account),
}
v.cache[email] = user
}
return user, found
}
func (v *userByEmail) Remove(email string) bool {
email = strings.ToLower(email)
v.Lock()
defer v.Unlock()
if _, found := v.cache[email]; !found {
return false
}
delete(v.cache, email)
return true
}
// Handler is an inbound connection handler that handles messages in VMess protocol.
type Handler struct {
inboundHandlerManager proxyman.InboundHandlerManager
policyManager core.PolicyManager
inboundHandlerManager core.InboundHandlerManager
clients protocol.UserValidator
usersByEmail *userByEmail
detours *DetourConfig
sessionHistory *encoding.SessionHistory
policyManager policy.Manager
}
// New creates a new VMess inbound handler.
func New(ctx context.Context, config *Config) (*Handler, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context")
v := core.FromContext(ctx)
if v == nil {
return nil, newError("V is not in context.")
}
handler := &Handler{
policyManager: v.PolicyManager(),
inboundHandlerManager: v.InboundHandlerManager(),
clients: vmess.NewTimedUserValidator(protocol.DefaultIDHash),
detours: config.Detour,
usersByEmail: newUserByEmail(config.GetDefaultValue()),
sessionHistory: encoding.NewSessionHistory(),
}
allowedClients := vmess.NewTimedUserValidator(ctx, protocol.DefaultIDHash)
for _, user := range config.User {
if err := allowedClients.Add(user); err != nil {
if err := handler.AddUser(ctx, user); err != nil {
return nil, newError("failed to initiate user").Base(err)
}
}
handler := &Handler{
clients: allowedClients,
detours: config.Detour,
usersByEmail: newUserByEmail(config.User, config.GetDefaultValue()),
sessionHistory: encoding.NewSessionHistory(ctx),
}
space.On(app.SpaceInitializing, func(interface{}) error {
handler.inboundHandlerManager = proxyman.InboundHandlerManagerFromSpace(space)
if handler.inboundHandlerManager == nil {
return newError("InboundHandlerManager is not found is space.")
}
handler.policyManager = policy.FromSpace(space)
if handler.policyManager == nil {
return newError("Policy is not found in space.")
}
return nil
})
return handler, nil
}
// Close implements common.Closable.
func (h *Handler) Close() error {
common.Close(h.clients)
common.Close(h.sessionHistory)
common.Close(h.usersByEmail)
return nil
}
// Network implements proxy.Inbound.Network().
func (*Handler) Network() net.NetworkList {
return net.NetworkList{
@ -133,6 +151,24 @@ func (h *Handler) GetUser(email string) *protocol.User {
return user
}
func (h *Handler) AddUser(ctx context.Context, user *protocol.User) error {
if len(user.Email) > 0 && !h.usersByEmail.Add(user) {
return newError("User ", user.Email, " already exists.")
}
return h.clients.Add(user)
}
func (h *Handler) RemoveUser(ctx context.Context, email string) error {
if len(email) == 0 {
return newError("Email must not be empty.")
}
if !h.usersByEmail.Remove(email) {
return newError("User ", email, " not found.")
}
h.clients.Remove(email)
return nil
}
func transferRequest(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output ray.OutputStream) error {
defer output.Close()
@ -179,9 +215,9 @@ 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 {
sessionPolicy := h.policyManager.GetPolicy(0)
if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeout.Handshake.Duration())); err != nil {
func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher core.Dispatcher) error {
sessionPolicy := h.policyManager.ForLevel(0)
if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil {
return newError("unable to set read deadline").Base(err).AtWarning()
}
@ -198,16 +234,11 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i
Status: log.AccessRejected,
Reason: err,
})
newError("invalid request from ", connection.RemoteAddr(), ": ", err).AtInfo().WriteToLog()
err = newError("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo()
}
return err
}
if request.Command == protocol.RequestCommandMux {
request.Address = net.DomainAddress("v1.mux.com")
request.Port = net.Port(0)
}
log.Record(&log.AccessMessage{
From: connection.RemoteAddr(),
To: request.Destination(),
@ -221,11 +252,11 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i
newError("unable to set back read deadline").Base(err).WriteToLog()
}
sessionPolicy = h.policyManager.GetPolicy(request.User.Level)
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
ctx = protocol.ContextWithUser(ctx, request.User)
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration())
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
ray, err := dispatcher.Dispatch(ctx, request.Destination())
if err != nil {
return newError("failed to dispatch request to ", request.Destination()).Base(err)
@ -235,14 +266,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())
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
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())
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
response := &protocol.ResponseHeader{
Command: h.generateCommand(ctx, request),
@ -265,7 +296,7 @@ func (h *Handler) generateCommand(ctx context.Context, request *protocol.Request
if h.inboundHandlerManager != nil {
handler, err := h.inboundHandlerManager.GetHandler(ctx, tag)
if err != nil {
newError("failed to get detour handler: ", tag, err).AtWarning().WriteToLog()
newError("failed to get detour handler: ", tag).Base(err).AtWarning().WriteToLog()
return nil
}
proxyHandler, port, availableMin := handler.GetRandomInboundProxy()

View File

@ -6,8 +6,7 @@ import (
"context"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/policy"
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
@ -23,17 +22,12 @@ import (
// Handler is an outbound connection handler for VMess protocol.
type Handler struct {
serverList *protocol.ServerList
serverPicker protocol.ServerPicker
policyManager policy.Manager
serverList *protocol.ServerList
serverPicker protocol.ServerPicker
v *core.Instance
}
func New(ctx context.Context, config *Config) (*Handler, error) {
space := app.SpaceFromContext(ctx)
if space == nil {
return nil, newError("no space in context.")
}
serverList := protocol.NewServerList()
for _, rec := range config.Receiver {
serverList.AddServer(protocol.NewServerSpecFromPB(*rec))
@ -41,16 +35,12 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
handler := &Handler{
serverList: serverList,
serverPicker: protocol.NewRoundRobinServerPicker(serverList),
v: core.FromContext(ctx),
}
space.On(app.SpaceInitializing, func(interface{}) error {
pm := policy.FromSpace(space)
if pm == nil {
return newError("Policy is not found in space.")
}
handler.policyManager = pm
return nil
})
if handler.v == nil {
return nil, newError("V is not in context.")
}
return handler, nil
}
@ -85,9 +75,9 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
if target.Network == net.Network_UDP {
command = protocol.RequestCommandUDP
}
if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.com" {
command = protocol.RequestCommandMux
}
//if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" {
// command = protocol.RequestCommandMux
//}
request := &protocol.RequestHeader{
Version: encoding.Version,
User: rec.PickUser(),
@ -112,10 +102,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)
sessionPolicy := v.v.PolicyManager().ForLevel(request.User.Level)
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeout.ConnectionIdle.Duration())
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
requestDone := signal.ExecuteAsync(func() error {
writer := buf.NewBufferedWriter(buf.NewWriter(conn))
@ -148,13 +138,13 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
return err
}
}
timer.SetTimeout(sessionPolicy.Timeout.DownlinkOnly.Duration())
timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
return nil
})
responseDone := signal.ExecuteAsync(func() error {
defer output.Close()
defer timer.SetTimeout(sessionPolicy.Timeout.UplinkOnly.Duration())
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
reader := buf.NewBufferedReader(buf.NewReader(conn))
header, err := session.DecodeResponseHeader(reader)

View File

@ -8,99 +8,110 @@ package vmess
//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg vmess -path Proxy,VMess
import (
"context"
"strings"
"sync"
"time"
"v2ray.com/core/common"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/signal"
)
const (
updateIntervalSec = 10
cacheDurationSec = 120
updateInterval = 10 * time.Second
cacheDurationSec = 120
)
type idEntry struct {
id *protocol.ID
userIdx int
lastSec protocol.Timestamp
lastSecRemoval protocol.Timestamp
type user struct {
user *protocol.User
account *InternalAccount
lastSec protocol.Timestamp
}
type TimedUserValidator struct {
sync.RWMutex
validUsers []*protocol.User
userHash map[[16]byte]indexTimePair
ids []*idEntry
hasher protocol.IDHash
baseTime protocol.Timestamp
users []*user
userHash map[[16]byte]indexTimePair
hasher protocol.IDHash
baseTime protocol.Timestamp
task *signal.PeriodicTask
}
type indexTimePair struct {
index int
user *user
timeInc uint32
}
func NewTimedUserValidator(ctx context.Context, hasher protocol.IDHash) protocol.UserValidator {
tus := &TimedUserValidator{
validUsers: make([]*protocol.User, 0, 16),
userHash: make(map[[16]byte]indexTimePair, 512),
ids: make([]*idEntry, 0, 512),
hasher: hasher,
baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*3),
func NewTimedUserValidator(hasher protocol.IDHash) protocol.UserValidator {
tuv := &TimedUserValidator{
users: make([]*user, 0, 16),
userHash: make(map[[16]byte]indexTimePair, 1024),
hasher: hasher,
baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*3),
}
go tus.updateUserHash(ctx, updateIntervalSec*time.Second)
return tus
tuv.task = &signal.PeriodicTask{
Interval: updateInterval,
Execute: func() error {
tuv.updateUserHash()
return nil
},
}
tuv.task.Start()
return tuv
}
func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, idx int, entry *idEntry) {
func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, user *user) {
var hashValue [16]byte
var hashValueRemoval [16]byte
idHash := v.hasher(entry.id.Bytes())
for entry.lastSec <= nowSec {
common.Must2(idHash.Write(entry.lastSec.Bytes(nil)))
idHash.Sum(hashValue[:0])
idHash.Reset()
genHashForID := func(id *protocol.ID) {
idHash := v.hasher(id.Bytes())
for ts := user.lastSec; ts <= nowSec; ts++ {
common.Must2(idHash.Write(ts.Bytes(nil)))
idHash.Sum(hashValue[:0])
idHash.Reset()
common.Must2(idHash.Write(entry.lastSecRemoval.Bytes(nil)))
idHash.Sum(hashValueRemoval[:0])
idHash.Reset()
delete(v.userHash, hashValueRemoval)
v.userHash[hashValue] = indexTimePair{
index: idx,
timeInc: uint32(entry.lastSec - v.baseTime),
}
entry.lastSec++
entry.lastSecRemoval++
}
}
func (v *TimedUserValidator) updateUserHash(ctx context.Context, interval time.Duration) {
for {
select {
case now := <-time.After(interval):
nowSec := protocol.Timestamp(now.Unix() + cacheDurationSec)
v.Lock()
for _, entry := range v.ids {
v.generateNewHashes(nowSec, entry.userIdx, entry)
v.userHash[hashValue] = indexTimePair{
user: user,
timeInc: uint32(ts - v.baseTime),
}
v.Unlock()
case <-ctx.Done():
return
}
}
genHashForID(user.account.ID)
for _, id := range user.account.AlterIDs {
genHashForID(id)
}
user.lastSec = nowSec
}
func (v *TimedUserValidator) removeExpiredHashes(expire uint32) {
for key, pair := range v.userHash {
if pair.timeInc < expire {
delete(v.userHash, key)
}
}
}
func (v *TimedUserValidator) Add(user *protocol.User) error {
func (v *TimedUserValidator) updateUserHash() {
now := time.Now()
nowSec := protocol.Timestamp(now.Unix() + cacheDurationSec)
v.Lock()
defer v.Unlock()
idx := len(v.validUsers)
v.validUsers = append(v.validUsers, user)
rawAccount, err := user.GetTypedAccount()
for _, user := range v.users {
v.generateNewHashes(nowSec, user)
}
expire := protocol.Timestamp(now.Unix() - cacheDurationSec*3)
if expire > v.baseTime {
v.removeExpiredHashes(uint32(expire - v.baseTime))
}
}
func (v *TimedUserValidator) Add(u *protocol.User) error {
v.Lock()
defer v.Unlock()
rawAccount, err := u.GetTypedAccount()
if err != nil {
return err
}
@ -108,24 +119,13 @@ func (v *TimedUserValidator) Add(user *protocol.User) error {
nowSec := time.Now().Unix()
entry := &idEntry{
id: account.ID,
userIdx: idx,
lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
lastSecRemoval: protocol.Timestamp(nowSec - cacheDurationSec*3),
}
v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry)
v.ids = append(v.ids, entry)
for _, alterid := range account.AlterIDs {
entry := &idEntry{
id: alterid,
userIdx: idx,
lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
lastSecRemoval: protocol.Timestamp(nowSec - cacheDurationSec*3),
}
v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry)
v.ids = append(v.ids, entry)
uu := &user{
user: u,
account: account,
lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
}
v.users = append(v.users, uu)
v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), uu)
return nil
}
@ -138,7 +138,36 @@ func (v *TimedUserValidator) Get(userHash []byte) (*protocol.User, protocol.Time
copy(fixedSizeHash[:], userHash)
pair, found := v.userHash[fixedSizeHash]
if found {
return v.validUsers[pair.index], protocol.Timestamp(pair.timeInc) + v.baseTime, true
return pair.user.user, protocol.Timestamp(pair.timeInc) + v.baseTime, true
}
return nil, 0, false
}
func (v *TimedUserValidator) Remove(email string) bool {
v.Lock()
defer v.Unlock()
email = strings.ToLower(email)
idx := -1
for i, u := range v.users {
if strings.ToLower(u.user.Email) == email {
idx = i
break
}
}
if idx == -1 {
return false
}
ulen := len(v.users)
if idx < len(v.users) {
v.users[idx] = v.users[ulen-1]
v.users[ulen-1] = nil
v.users = v.users[:ulen-1]
}
return true
}
// Close implements common.Closable.
func (v *TimedUserValidator) Close() error {
return v.task.Close()
}

58
proxy/vmess/vmess_test.go Normal file
View File

@ -0,0 +1,58 @@
package vmess_test
import (
"testing"
"time"
"v2ray.com/core/common"
"v2ray.com/core/common/serial"
"v2ray.com/core/common/uuid"
"v2ray.com/core/common/protocol"
. "v2ray.com/core/proxy/vmess"
. "v2ray.com/ext/assert"
)
func TestUserValidator(t *testing.T) {
assert := With(t)
hasher := protocol.DefaultIDHash
v := NewTimedUserValidator(hasher)
defer common.Close(v)
id := uuid.New()
user := &protocol.User{
Email: "test",
Account: serial.ToTypedMessage(&Account{
Id: id.String(),
AlterId: 8,
}),
}
common.Must(v.Add(user))
{
ts := protocol.Timestamp(time.Now().Unix())
idHash := hasher(id.Bytes())
idHash.Write(ts.Bytes(nil))
userHash := idHash.Sum(nil)
euser, ets, found := v.Get(userHash)
assert(found, IsTrue)
assert(euser.Email, Equals, user.Email)
assert(int64(ets), Equals, int64(ts))
}
{
ts := protocol.Timestamp(time.Now().Add(time.Second * 500).Unix())
idHash := hasher(id.Bytes())
idHash.Write(ts.Bytes(nil))
userHash := idHash.Sum(nil)
euser, _, found := v.Get(userHash)
assert(found, IsFalse)
assert(euser, IsNil)
}
assert(v.Remove(user.Email), IsTrue)
assert(v.Remove(user.Email), IsFalse)
}

View File

@ -25,7 +25,7 @@ echo ${SIGN_KEY_PASS} | gpg --passphrase-fd 0 --batch --import /v2ray/build/sign
curl -L -o /v2ray/build/releases https://api.github.com/repos/v2ray/v2ray-core/releases
GO_INSTALL=golang.tar.gz
curl -L -o ${GO_INSTALL} https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz
curl -L -o ${GO_INSTALL} https://storage.googleapis.com/golang/go1.9.4.linux-amd64.tar.gz
tar -C /usr/local -xzf ${GO_INSTALL}
export PATH=$PATH:/usr/local/go/bin
@ -65,7 +65,7 @@ $GOPATH/bin/vbuild --os=openbsd --arch=amd64 --zip --sign #--encrypt
RELBODY="https://www.v2ray.com/chapter_00/01_versions.html"
JSON_DATA=$(echo "{}" | jq -c ".tag_name=\"${RELEASE_TAG}\"")
JSON_DATA=$(echo ${JSON_DATA} | jq -c ".prerelease=${PRERELEASE}")
JSON_DATA=$(echo ${JSON_DATA} | jq -c ".body=${RELBODY}")
JSON_DATA=$(echo ${JSON_DATA} | jq -c ".body=\"${RELBODY}\"")
RELEASE_ID=$(curl --data "${JSON_DATA}" -H "Authorization: token ${GITHUB_TOKEN}" -X POST https://api.github.com/repos/v2ray/v2ray-core/releases | jq ".id")
function upload() {

115
router.go Normal file
View File

@ -0,0 +1,115 @@
package core
import (
"context"
"sync"
"v2ray.com/core/common"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/net"
"v2ray.com/core/transport/ray"
)
// Dispatcher is a feature that dispatches inbound requests to outbound handlers based on rules.
// Dispatcher is required to be registered in a V2Ray instance to make V2Ray function properly.
type Dispatcher interface {
Feature
// Dispatch returns a Ray for transporting data for the given request.
Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error)
}
type syncDispatcher struct {
sync.RWMutex
Dispatcher
}
func (d *syncDispatcher) Dispatch(ctx context.Context, dest net.Destination) (ray.InboundRay, error) {
d.RLock()
defer d.RUnlock()
if d.Dispatcher == nil {
return nil, newError("Dispatcher not set.").AtError()
}
return d.Dispatcher.Dispatch(ctx, dest)
}
func (d *syncDispatcher) Start() error {
d.RLock()
defer d.RUnlock()
if d.Dispatcher == nil {
return newError("Dispatcher not set.").AtError()
}
return d.Dispatcher.Start()
}
func (d *syncDispatcher) Close() error {
d.RLock()
defer d.RUnlock()
return common.Close(d.Dispatcher)
}
func (d *syncDispatcher) Set(disp Dispatcher) {
d.Lock()
defer d.Unlock()
d.Dispatcher = disp
}
var (
// ErrNoClue is for the situation that existing information is not enough to make a decision. For example, Router may return this error when there is no suitable route.
ErrNoClue = errors.New("not enough information for making a decision")
)
// Router is a feature to choose a outbound tag for the given request.
type Router interface {
Feature
// PickRoute returns a tag of an OutboundHandler based on the given context.
PickRoute(ctx context.Context) (string, error)
}
type syncRouter struct {
sync.RWMutex
Router
}
func (r *syncRouter) PickRoute(ctx context.Context) (string, error) {
r.RLock()
defer r.RUnlock()
if r.Router == nil {
return "", ErrNoClue
}
return r.Router.PickRoute(ctx)
}
func (r *syncRouter) Start() error {
r.RLock()
defer r.RUnlock()
if r.Router == nil {
return nil
}
return r.Router.Start()
}
func (r *syncRouter) Close() error {
r.RLock()
defer r.RUnlock()
return common.Close(r.Router)
}
func (r *syncRouter) Set(router Router) {
r.Lock()
defer r.Unlock()
r.Router = router
}

View File

@ -0,0 +1,130 @@
package scenarios
import (
"context"
"fmt"
"testing"
"google.golang.org/grpc"
"v2ray.com/core"
"v2ray.com/core/app/commander"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/app/proxyman/command"
"v2ray.com/core/app/router"
"v2ray.com/core/common/net"
"v2ray.com/core/common/serial"
"v2ray.com/core/proxy/dokodemo"
"v2ray.com/core/proxy/freedom"
"v2ray.com/core/testing/servers/tcp"
. "v2ray.com/ext/assert"
)
func TestCommanderRemoveHandler(t *testing.T) {
assert := With(t)
tcpServer := tcp.Server{
MsgProcessor: xor,
}
dest, err := tcpServer.Start()
assert(err, IsNil)
defer tcpServer.Close()
clientPort := pickPort()
cmdPort := pickPort()
clientConfig := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&commander.Config{
Tag: "api",
Service: []*serial.TypedMessage{
serial.ToTypedMessage(&command.Config{}),
},
}),
serial.ToTypedMessage(&router.Config{
Rule: []*router.RoutingRule{
{
InboundTag: []string{"api"},
Tag: "api",
},
},
}),
},
Inbound: []*core.InboundHandlerConfig{
{
Tag: "d",
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortRange: net.SinglePortRange(clientPort),
Listen: net.NewIPOrDomain(net.LocalHostIP),
}),
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
Address: net.NewIPOrDomain(dest.Address),
Port: uint32(dest.Port),
NetworkList: &net.NetworkList{
Network: []net.Network{net.Network_TCP},
},
}),
},
{
Tag: "api",
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortRange: net.SinglePortRange(cmdPort),
Listen: net.NewIPOrDomain(net.LocalHostIP),
}),
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
Address: net.NewIPOrDomain(dest.Address),
Port: uint32(dest.Port),
NetworkList: &net.NetworkList{
Network: []net.Network{net.Network_TCP},
},
}),
},
},
Outbound: []*core.OutboundHandlerConfig{
{
Tag: "default-outbound",
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
servers, err := InitializeServerConfigs(clientConfig)
assert(err, IsNil)
{
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: int(clientPort),
})
assert(err, IsNil)
payload := "commander request."
nBytes, err := conn.Write([]byte(payload))
assert(err, IsNil)
assert(nBytes, Equals, len(payload))
response := make([]byte, 1024)
nBytes, err = conn.Read(response)
assert(err, IsNil)
assert(response[:nBytes], Equals, xor([]byte(payload)))
assert(conn.Close(), IsNil)
}
cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure())
assert(err, IsNil)
hsClient := command.NewHandlerServiceClient(cmdConn)
resp, err := hsClient.RemoveInbound(context.Background(), &command.RemoveInboundRequest{
Tag: "d",
})
assert(err, IsNil)
assert(resp, IsNotNil)
{
_, err := net.DialTCP("tcp", nil, &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: int(clientPort),
})
assert(err, IsNotNil)
}
CloseAllServers(servers)
}

View File

@ -13,14 +13,17 @@ import (
"github.com/golang/protobuf/proto"
"v2ray.com/core"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common"
"v2ray.com/core/common/log"
"v2ray.com/core/common/net"
"v2ray.com/core/common/retry"
"v2ray.com/core/common/serial"
)
func pickPort() net.Port {
listener, err := net.Listen("tcp4", ":0")
listener, err := net.Listen("tcp4", "127.0.0.1:0")
common.Must(err)
defer listener.Close()
@ -70,6 +73,7 @@ func InitializeServerConfig(config *core.Config) (*exec.Cmd, error) {
return nil, err
}
config = withDefaultApps(config)
configBytes, err := proto.Marshal(config)
if err != nil {
return nil, err
@ -128,3 +132,10 @@ func CloseAllServers(servers []*exec.Cmd) {
Content: "All server closed.",
})
}
func withDefaultApps(config *core.Config) *core.Config {
config.App = append(config.App, serial.ToTypedMessage(&dispatcher.Config{}))
config.App = append(config.App, serial.ToTypedMessage(&proxyman.InboundConfig{}))
config.App = append(config.App, serial.ToTypedMessage(&proxyman.OutboundConfig{}))
return config
}

View File

@ -51,7 +51,7 @@ func TestResolveIP(t *testing.T) {
},
}),
},
Inbound: []*proxyman.InboundHandlerConfig{
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortRange: net.SinglePortRange(serverPort),
@ -67,7 +67,7 @@ func TestResolveIP(t *testing.T) {
}),
},
},
Outbound: []*proxyman.OutboundHandlerConfig{
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&blackhole.Config{}),
},

View File

@ -40,7 +40,7 @@ func TestDokodemoTCP(t *testing.T) {
ErrorLogType: log.LogType_Console,
}),
},
Inbound: []*proxyman.InboundHandlerConfig{
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortRange: net.SinglePortRange(serverPort),
@ -57,7 +57,7 @@ func TestDokodemoTCP(t *testing.T) {
}),
},
},
Outbound: []*proxyman.OutboundHandlerConfig{
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
},
@ -73,7 +73,7 @@ func TestDokodemoTCP(t *testing.T) {
ErrorLogType: log.LogType_Console,
}),
},
Inbound: []*proxyman.InboundHandlerConfig{
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortRange: &net.PortRange{From: clientPort, To: clientPort + clientPortRange},
@ -88,7 +88,7 @@ func TestDokodemoTCP(t *testing.T) {
}),
},
},
Outbound: []*proxyman.OutboundHandlerConfig{
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&outbound.Config{
Receiver: []*protocol.ServerEndpoint{
@ -147,7 +147,7 @@ func TestDokodemoUDP(t *testing.T) {
userID := protocol.NewID(uuid.New())
serverPort := pickPort()
serverConfig := &core.Config{
Inbound: []*proxyman.InboundHandlerConfig{
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortRange: net.SinglePortRange(serverPort),
@ -164,7 +164,7 @@ func TestDokodemoUDP(t *testing.T) {
}),
},
},
Outbound: []*proxyman.OutboundHandlerConfig{
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
},
@ -174,7 +174,7 @@ func TestDokodemoUDP(t *testing.T) {
clientPort := uint32(pickPort())
clientPortRange := uint32(5)
clientConfig := &core.Config{
Inbound: []*proxyman.InboundHandlerConfig{
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortRange: &net.PortRange{From: clientPort, To: clientPort + clientPortRange},
@ -189,7 +189,7 @@ func TestDokodemoUDP(t *testing.T) {
}),
},
},
Outbound: []*proxyman.OutboundHandlerConfig{
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&outbound.Config{
Receiver: []*protocol.ServerEndpoint{

Some files were not shown because too many files have changed in this diff Show More