From ca721230e15aa6b3c34f8e80591cc5bb4a9c589f Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 26 Jan 2017 20:46:44 +0100 Subject: [PATCH] split listening settings from inbound proxies and apply context --- app/dispatcher/dispatcher.go | 5 +- app/dispatcher/impl/default.go | 20 +- app/dns/server/nameserver.go | 17 +- app/proxy/config.pb.go | 42 --- app/proxy/config.proto | 10 - app/proxy/proxy.go | 156 --------- app/proxy/proxy_test.go | 74 ----- app/proxyman/config.pb.go | 239 ++++++-------- app/proxyman/config.proto | 24 +- app/proxyman/inbound/always.go | 78 +++++ app/proxyman/inbound/dynamic.go | 143 ++++++++ app/proxyman/inbound/inbound.go | 94 ++++++ app/proxyman/inbound/worker.go | 269 +++++++++++++++ app/proxyman/outbound/handler.go | 177 +++++++--- app/proxyman/outbound/outbound.go | 33 +- app/proxyman/proxyman.go | 19 +- app/router/condition.go | 91 ++++-- app/router/config.go | 6 +- app/router/router.go | 35 +- app/router/router_test.go | 3 +- common/net/destination.go | 4 + common/protocol/context.go | 23 ++ common/type.go | 2 +- config.go | 55 ---- config.pb.go | 307 ++---------------- config.proto | 74 +---- inbound_detour.go | 11 - inbound_detour_always.go | 77 ----- inbound_detour_dynamic.go | 164 ---------- main/distro/all/all.go | 2 +- proxy/blackhole/blackhole.go | 10 +- proxy/context.go | 128 ++++++-- proxy/dokodemo/dokodemo.go | 165 ++-------- proxy/dokodemo/dokodemo_test.go | 173 ---------- proxy/freedom/freedom.go | 22 +- proxy/freedom/freedom_test.go | 97 ------ proxy/http/server.go | 89 ++--- proxy/proxy.go | 49 +-- proxy/shadowsocks/client.go | 49 +-- proxy/shadowsocks/server.go | 160 ++++----- proxy/socks/client.go | 25 +- proxy/socks/protocol.go | 5 +- proxy/socks/server.go | 162 +++++---- proxy/socks/server_udp.go | 66 ---- proxy/vmess/inbound/command.go | 38 --- proxy/vmess/inbound/inbound.go | 118 +++---- proxy/vmess/outbound/outbound.go | 22 +- testing/scenarios/dokodemo_test.go | 57 ++-- testing/scenarios/feature_test.go | 169 ++++++---- testing/scenarios/socks_test.go | 79 +++-- testing/scenarios/tls_test.go | 185 ++++++----- testing/scenarios/transport_test.go | 67 ++-- tools/conf/router_test.go | 94 ++---- tools/conf/v2ray.go | 163 ++++++---- transport/internet/config.go | 18 + transport/internet/context.go | 52 +++ transport/internet/dialer.go | 39 +-- transport/internet/kcp/dialer.go | 18 +- transport/internet/kcp/kcp_test.go | 14 +- transport/internet/kcp/listener.go | 5 +- transport/internet/tcp/dialer.go | 21 +- transport/internet/tcp/hub.go | 1 + .../internet/tcp}/sockopt_linux.go | 2 +- .../internet/tcp}/sockopt_other.go | 2 +- transport/internet/tcp_hub.go | 3 +- transport/internet/udp/dialer.go | 5 +- transport/internet/udp/hub.go | 26 +- transport/internet/udp/udp_server.go | 148 ++------- transport/internet/websocket/dialer.go | 35 +- transport/internet/websocket/ws_test.go | 73 +---- transport/ray/direct.go | 24 +- transport/ray/direct_test.go | 6 +- v2ray.go | 105 +----- 73 files changed, 2086 insertions(+), 2957 deletions(-) delete mode 100644 app/proxy/config.pb.go delete mode 100644 app/proxy/config.proto delete mode 100644 app/proxy/proxy.go delete mode 100644 app/proxy/proxy_test.go create mode 100644 app/proxyman/inbound/always.go create mode 100644 app/proxyman/inbound/dynamic.go create mode 100644 app/proxyman/inbound/inbound.go create mode 100644 app/proxyman/inbound/worker.go create mode 100644 common/protocol/context.go delete mode 100644 config.go delete mode 100644 inbound_detour.go delete mode 100644 inbound_detour_always.go delete mode 100644 inbound_detour_dynamic.go delete mode 100644 proxy/dokodemo/dokodemo_test.go delete mode 100644 proxy/freedom/freedom_test.go delete mode 100644 proxy/socks/server_udp.go delete mode 100644 proxy/vmess/inbound/command.go create mode 100644 transport/internet/context.go rename {proxy/dokodemo => transport/internet/tcp}/sockopt_linux.go (98%) rename {proxy/dokodemo => transport/internet/tcp}/sockopt_other.go (92%) diff --git a/app/dispatcher/dispatcher.go b/app/dispatcher/dispatcher.go index 2f36ad874..91516fc7a 100644 --- a/app/dispatcher/dispatcher.go +++ b/app/dispatcher/dispatcher.go @@ -1,14 +1,15 @@ package dispatcher import ( + "context" + "v2ray.com/core/app" - "v2ray.com/core/proxy" "v2ray.com/core/transport/ray" ) // Interface dispatch a packet and possibly further network payload to its destination. type Interface interface { - DispatchToOutbound(session *proxy.SessionInfo) ray.InboundRay + DispatchToOutbound(ctx context.Context) ray.InboundRay } func FromSpace(space app.Space) Interface { diff --git a/app/dispatcher/impl/default.go b/app/dispatcher/impl/default.go index 5aa69f8ee..b38f02dcc 100644 --- a/app/dispatcher/impl/default.go +++ b/app/dispatcher/impl/default.go @@ -12,7 +12,6 @@ import ( "v2ray.com/core/common/buf" "v2ray.com/core/common/errors" "v2ray.com/core/common/log" - "v2ray.com/core/common/net" "v2ray.com/core/proxy" "v2ray.com/core/transport/ray" ) @@ -43,12 +42,15 @@ func (DefaultDispatcher) Interface() interface{} { return (*dispatcher.Interface)(nil) } -func (v *DefaultDispatcher) DispatchToOutbound(session *proxy.SessionInfo) ray.InboundRay { +func (v *DefaultDispatcher) DispatchToOutbound(ctx context.Context) ray.InboundRay { dispatcher := v.ohm.GetDefaultHandler() - destination := session.Destination + destination := proxy.DestinationFromContext(ctx) + if !destination.IsValid() { + panic("Dispatcher: Invalid destination.") + } if v.router != nil { - if tag, err := v.router.TakeDetour(session); err == nil { + if tag, err := v.router.TakeDetour(ctx); err == nil { if handler := v.ohm.GetHandler(tag); handler != nil { log.Info("DefaultDispatcher: Taking detour [", tag, "] for [", destination, "].") dispatcher = handler @@ -60,9 +62,9 @@ func (v *DefaultDispatcher) DispatchToOutbound(session *proxy.SessionInfo) ray.I } } - direct := ray.NewRay() + direct := ray.NewRay(ctx) var waitFunc func() error - if session.Inbound != nil && session.Inbound.AllowPassiveConnection { + if allowPassiveConnection, ok := proxy.AllowPassiveConnectionFromContext(ctx); ok && allowPassiveConnection { waitFunc = noOpWait() } else { wdi := &waitDataInspector{ @@ -72,12 +74,12 @@ func (v *DefaultDispatcher) DispatchToOutbound(session *proxy.SessionInfo) ray.I waitFunc = waitForData(wdi) } - go v.waitAndDispatch(waitFunc, destination, direct, dispatcher) + go v.waitAndDispatch(ctx, waitFunc, direct, dispatcher) return direct } -func (v *DefaultDispatcher) waitAndDispatch(wait func() error, destination net.Destination, link ray.OutboundRay, dispatcher proxy.OutboundHandler) { +func (v *DefaultDispatcher) waitAndDispatch(ctx context.Context, wait func() error, link ray.OutboundRay, dispatcher proxyman.OutboundHandler) { if err := wait(); err != nil { log.Info("DefaultDispatcher: Failed precondition: ", err) link.OutboundInput().CloseError() @@ -85,7 +87,7 @@ func (v *DefaultDispatcher) waitAndDispatch(wait func() error, destination net.D return } - dispatcher.Dispatch(destination, link) + dispatcher.Dispatch(ctx, link) } func init() { diff --git a/app/dns/server/nameserver.go b/app/dns/server/nameserver.go index fe78a36ff..561db73ce 100644 --- a/app/dns/server/nameserver.go +++ b/app/dns/server/nameserver.go @@ -1,19 +1,18 @@ package server import ( + "context" "net" "sync" "time" + "github.com/miekg/dns" "v2ray.com/core/app/dispatcher" "v2ray.com/core/common/buf" "v2ray.com/core/common/dice" "v2ray.com/core/common/log" v2net "v2ray.com/core/common/net" - "v2ray.com/core/proxy" "v2ray.com/core/transport/internet/udp" - - "github.com/miekg/dns" ) const ( @@ -101,7 +100,7 @@ func (v *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 { } // Private: Visible for testing. -func (v *UDPNameServer) HandleResponse(dest v2net.Destination, payload *buf.Buffer) { +func (v *UDPNameServer) HandleResponse(payload *buf.Buffer) { msg := new(dns.Msg) err := msg.Unpack(payload.Bytes()) if err != nil { @@ -165,15 +164,12 @@ func (v *UDPNameServer) BuildQueryA(domain string, id uint16) *buf.Buffer { return buffer } -func (v *UDPNameServer) DispatchQuery(payload *buf.Buffer) { - v.udpServer.Dispatch(&proxy.SessionInfo{Source: pseudoDestination, Destination: v.address}, payload, v.HandleResponse) -} - func (v *UDPNameServer) QueryA(domain string) <-chan *ARecord { response := make(chan *ARecord, 1) id := v.AssignUnusedID(response) - v.DispatchQuery(v.BuildQueryA(domain, id)) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*8) + v.udpServer.Dispatch(ctx, v.address, v.BuildQueryA(domain, id), v.HandleResponse) go func() { for i := 0; i < 2; i++ { @@ -182,11 +178,12 @@ func (v *UDPNameServer) QueryA(domain string) <-chan *ARecord { _, found := v.requests[id] v.Unlock() if found { - v.DispatchQuery(v.BuildQueryA(domain, id)) + v.udpServer.Dispatch(ctx, v.address, v.BuildQueryA(domain, id), v.HandleResponse) } else { break } } + cancel() }() return response diff --git a/app/proxy/config.pb.go b/app/proxy/config.pb.go deleted file mode 100644 index dfc5c99a7..000000000 --- a/app/proxy/config.pb.go +++ /dev/null @@ -1,42 +0,0 @@ -package proxy - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type Config struct { -} - -func (m *Config) Reset() { *m = Config{} } -func (m *Config) String() string { return proto.CompactTextString(m) } -func (*Config) ProtoMessage() {} -func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -func init() { - proto.RegisterType((*Config)(nil), "v2ray.core.app.proxy.Config") -} - -func init() { proto.RegisterFile("v2ray.com/core/app/proxy/config.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 128 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x33, 0x2a, 0x4a, - 0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0x2f, - 0x28, 0xca, 0xaf, 0xa8, 0xd4, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, - 0xc9, 0x17, 0x12, 0x81, 0x29, 0x2b, 0x4a, 0xd5, 0x4b, 0x2c, 0x28, 0xd0, 0x03, 0x2b, 0x51, 0xe2, - 0xe0, 0x62, 0x73, 0x06, 0xab, 0x72, 0x72, 0xe5, 0x92, 0x48, 0xce, 0xcf, 0xd5, 0xc3, 0xa6, 0xca, - 0x89, 0x1b, 0xa2, 0x26, 0x00, 0x64, 0x50, 0x14, 0x2b, 0x58, 0x6c, 0x15, 0x93, 0x48, 0x98, 0x51, - 0x50, 0x62, 0xa5, 0x9e, 0x33, 0x48, 0xa9, 0x63, 0x41, 0x81, 0x5e, 0x00, 0x48, 0x38, 0x89, 0x0d, - 0x6c, 0x9b, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x16, 0xbe, 0x54, 0x50, 0x96, 0x00, 0x00, 0x00, -} diff --git a/app/proxy/config.proto b/app/proxy/config.proto deleted file mode 100644 index c2616aa90..000000000 --- a/app/proxy/config.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto3"; - -package v2ray.core.app.proxy; -option csharp_namespace = "V2Ray.Core.App.Proxy"; -option go_package = "proxy"; -option java_package = "com.v2ray.core.app.proxy"; -option java_outer_classname = "ConfigProto"; - -message Config { -} diff --git a/app/proxy/proxy.go b/app/proxy/proxy.go deleted file mode 100644 index 667f7d99b..000000000 --- a/app/proxy/proxy.go +++ /dev/null @@ -1,156 +0,0 @@ -package proxy - -import ( - "io" - "net" - "time" - - "context" - - "v2ray.com/core/app" - "v2ray.com/core/app/proxyman" - "v2ray.com/core/common" - "v2ray.com/core/common/buf" - "v2ray.com/core/common/errors" - "v2ray.com/core/common/log" - v2net "v2ray.com/core/common/net" - "v2ray.com/core/transport/internet" - "v2ray.com/core/transport/ray" -) - -type OutboundProxy struct { - outboundManager proxyman.OutboundHandlerManager -} - -func NewOutboundProxy(ctx context.Context, config *Config) (*OutboundProxy, error) { - space := app.SpaceFromContext(ctx) - if space == nil { - return nil, errors.New("OutboundProxy: No space in context.") - } - proxy := new(OutboundProxy) - space.OnInitialize(func() error { - proxy.outboundManager = proxyman.OutboundHandlerManagerFromSpace(space) - if proxy.outboundManager == nil { - return errors.New("Proxy: Outbound handler manager not found in space.") - } - return nil - }) - return proxy, nil -} - -func (OutboundProxy) Interface() interface{} { - return (*OutboundProxy)(nil) -} - -func (v *OutboundProxy) RegisterDialer() { - internet.ProxyDialer = v.Dial -} - -// Dial implements internet.Dialer. -func (v *OutboundProxy) Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (internet.Connection, error) { - handler := v.outboundManager.GetHandler(options.Proxy.Tag) - if handler == nil { - log.Warning("Proxy: Failed to get outbound handler with tag: ", options.Proxy.Tag) - return internet.Dial(src, dest, internet.DialerOptions{ - Stream: options.Stream, - }) - } - log.Info("Proxy: Dialing to ", dest) - stream := ray.NewRay() - go handler.Dispatch(dest, stream) - return NewConnection(src, dest, stream), nil -} - -type Connection struct { - stream ray.Ray - closed bool - localAddr net.Addr - remoteAddr net.Addr - - reader *buf.BufferToBytesReader - writer *buf.BytesToBufferWriter -} - -func NewConnection(src v2net.Address, dest v2net.Destination, 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.NewBytesReader(stream.InboundOutput()), - writer: buf.NewBytesWriter(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) -} - -// Write implements net.Conn.Write(). -func (v *Connection) Write(b []byte) (int, error) { - if v.closed { - return 0, io.ErrClosedPipe - } - return v.writer.Write(b) -} - -// Close implements net.Conn.Close(). -func (v *Connection) Close() error { - v.closed = true - v.stream.InboundInput().Close() - v.stream.InboundOutput().CloseError() - 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 -} - -func (v *Connection) SetDeadline(t time.Time) error { - return nil -} - -func (v *Connection) SetReadDeadline(t time.Time) error { - return nil -} - -func (v *Connection) SetWriteDeadline(t time.Time) error { - return nil -} - -func (v *Connection) Reusable() bool { - return false -} - -func (v *Connection) SetReusable(bool) { - -} - -func OutboundProxyFromSpace(space app.Space) *OutboundProxy { - app := space.GetApplication((*OutboundProxy)(nil)) - if app == nil { - return nil - } - return app.(*OutboundProxy) -} - -func init() { - common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { - return NewOutboundProxy(ctx, config.(*Config)) - })) -} diff --git a/app/proxy/proxy_test.go b/app/proxy/proxy_test.go deleted file mode 100644 index f231f5249..000000000 --- a/app/proxy/proxy_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package proxy_test - -import ( - "context" - "testing" - - "v2ray.com/core/app" - . "v2ray.com/core/app/proxy" - "v2ray.com/core/app/proxyman" - _ "v2ray.com/core/app/proxyman/outbound" - "v2ray.com/core/common" - "v2ray.com/core/common/net" - "v2ray.com/core/proxy" - "v2ray.com/core/proxy/freedom" - "v2ray.com/core/testing/assert" - "v2ray.com/core/testing/servers/tcp" - "v2ray.com/core/transport/internet" - _ "v2ray.com/core/transport/internet/tcp" -) - -func TestProxyDial(t *testing.T) { - assert := assert.On(t) - - space := app.NewSpace() - ctx := app.ContextWithSpace(context.Background(), space) - assert.Error(app.AddApplicationToSpace(ctx, new(proxyman.OutboundConfig))).IsNil() - outboundManager := proxyman.OutboundHandlerManagerFromSpace(space) - freedom, err := freedom.New(proxy.ContextWithOutboundMeta(ctx, &proxy.OutboundHandlerMeta{ - Tag: "tag", - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_TCP, - }, - }), &freedom.Config{}) - assert.Error(err).IsNil() - common.Must(outboundManager.SetHandler("tag", freedom)) - - assert.Error(app.AddApplicationToSpace(ctx, new(Config))).IsNil() - proxy := OutboundProxyFromSpace(space) - assert.Error(space.Initialize()).IsNil() - - xor := func(b []byte) []byte { - for idx, x := range b { - b[idx] = x ^ 'c' - } - return b - } - tcpServer := &tcp.Server{ - MsgProcessor: xor, - } - dest, err := tcpServer.Start() - assert.Error(err).IsNil() - - conn, err := proxy.Dial(net.LocalHostIP, dest, internet.DialerOptions{ - Stream: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_TCP, - }, - Proxy: &internet.ProxyConfig{ - Tag: "tag", - }, - }) - assert.Error(err).IsNil() - - _, err = conn.Write([]byte{'a', 'b', 'c', 'd'}) - assert.Error(err).IsNil() - - b := make([]byte, 10) - nBytes, err := conn.Read(b) - assert.Error(err).IsNil() - - assert.Bytes(xor(b[:nBytes])).Equals([]byte{'a', 'b', 'c', 'd'}) - - common.Must(conn.Close()) - tcpServer.Close() -} diff --git a/app/proxyman/config.pb.go b/app/proxyman/config.pb.go index 4b51106f5..ce251f51f 100644 --- a/app/proxyman/config.pb.go +++ b/app/proxyman/config.pb.go @@ -134,88 +134,72 @@ func (m *AllocationStrategy_AllocationStrategyRefresh) GetValue() uint32 { return 0 } -type StreamReceiverConfig struct { - PortRange *v2ray_core_common_net1.PortRange `protobuf:"bytes,1,opt,name=port_range,json=portRange" json:"port_range,omitempty"` - Listen *v2ray_core_common_net.IPOrDomain `protobuf:"bytes,2,opt,name=listen" json:"listen,omitempty"` - AllocationStrategy *AllocationStrategy `protobuf:"bytes,3,opt,name=allocation_strategy,json=allocationStrategy" json:"allocation_strategy,omitempty"` - StreamSettings *v2ray_core_transport_internet.StreamConfig `protobuf:"bytes,4,opt,name=stream_settings,json=streamSettings" json:"stream_settings,omitempty"` +type ReceiverConfig struct { + PortRange *v2ray_core_common_net1.PortRange `protobuf:"bytes,1,opt,name=port_range,json=portRange" json:"port_range,omitempty"` + Listen *v2ray_core_common_net.IPOrDomain `protobuf:"bytes,2,opt,name=listen" json:"listen,omitempty"` + AllocationStrategy *AllocationStrategy `protobuf:"bytes,3,opt,name=allocation_strategy,json=allocationStrategy" json:"allocation_strategy,omitempty"` + StreamSettings *v2ray_core_transport_internet.StreamConfig `protobuf:"bytes,4,opt,name=stream_settings,json=streamSettings" json:"stream_settings,omitempty"` + ReceiveOriginalDestination bool `protobuf:"varint,5,opt,name=receive_original_destination,json=receiveOriginalDestination" json:"receive_original_destination,omitempty"` + AllowPassiveConnection bool `protobuf:"varint,6,opt,name=allow_passive_connection,json=allowPassiveConnection" json:"allow_passive_connection,omitempty"` } -func (m *StreamReceiverConfig) Reset() { *m = StreamReceiverConfig{} } -func (m *StreamReceiverConfig) String() string { return proto.CompactTextString(m) } -func (*StreamReceiverConfig) ProtoMessage() {} -func (*StreamReceiverConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (m *ReceiverConfig) Reset() { *m = ReceiverConfig{} } +func (m *ReceiverConfig) String() string { return proto.CompactTextString(m) } +func (*ReceiverConfig) ProtoMessage() {} +func (*ReceiverConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } -func (m *StreamReceiverConfig) GetPortRange() *v2ray_core_common_net1.PortRange { +func (m *ReceiverConfig) GetPortRange() *v2ray_core_common_net1.PortRange { if m != nil { return m.PortRange } return nil } -func (m *StreamReceiverConfig) GetListen() *v2ray_core_common_net.IPOrDomain { +func (m *ReceiverConfig) GetListen() *v2ray_core_common_net.IPOrDomain { if m != nil { return m.Listen } return nil } -func (m *StreamReceiverConfig) GetAllocationStrategy() *AllocationStrategy { +func (m *ReceiverConfig) GetAllocationStrategy() *AllocationStrategy { if m != nil { return m.AllocationStrategy } return nil } -func (m *StreamReceiverConfig) GetStreamSettings() *v2ray_core_transport_internet.StreamConfig { +func (m *ReceiverConfig) GetStreamSettings() *v2ray_core_transport_internet.StreamConfig { if m != nil { return m.StreamSettings } return nil } -type DatagramReceiverConfig struct { - PortRange *v2ray_core_common_net1.PortRange `protobuf:"bytes,1,opt,name=port_range,json=portRange" json:"port_range,omitempty"` - Listen *v2ray_core_common_net.IPOrDomain `protobuf:"bytes,2,opt,name=listen" json:"listen,omitempty"` - AllocationStrategy *AllocationStrategy `protobuf:"bytes,3,opt,name=allocation_strategy,json=allocationStrategy" json:"allocation_strategy,omitempty"` +func (m *ReceiverConfig) GetReceiveOriginalDestination() bool { + if m != nil { + return m.ReceiveOriginalDestination + } + return false } -func (m *DatagramReceiverConfig) Reset() { *m = DatagramReceiverConfig{} } -func (m *DatagramReceiverConfig) String() string { return proto.CompactTextString(m) } -func (*DatagramReceiverConfig) ProtoMessage() {} -func (*DatagramReceiverConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } - -func (m *DatagramReceiverConfig) GetPortRange() *v2ray_core_common_net1.PortRange { +func (m *ReceiverConfig) GetAllowPassiveConnection() bool { if m != nil { - return m.PortRange + return m.AllowPassiveConnection } - return nil -} - -func (m *DatagramReceiverConfig) GetListen() *v2ray_core_common_net.IPOrDomain { - if m != nil { - return m.Listen - } - return nil -} - -func (m *DatagramReceiverConfig) GetAllocationStrategy() *AllocationStrategy { - if m != nil { - return m.AllocationStrategy - } - return nil + return false } type InboundHandlerConfig struct { - Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` - ReceiverSettings []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,rep,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"` + 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{4} } +func (*InboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } func (m *InboundHandlerConfig) GetTag() string { if m != nil { @@ -224,7 +208,7 @@ func (m *InboundHandlerConfig) GetTag() string { return "" } -func (m *InboundHandlerConfig) GetReceiverSettings() []*v2ray_core_common_serial.TypedMessage { +func (m *InboundHandlerConfig) GetReceiverSettings() *v2ray_core_common_serial.TypedMessage { if m != nil { return m.ReceiverSettings } @@ -244,60 +228,35 @@ 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{5} } +func (*OutboundConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } -type StreamSenderConfig struct { +type SenderConfig struct { // Send traffic through the given IP. Only IP is allowed. Via *v2ray_core_common_net.IPOrDomain `protobuf:"bytes,1,opt,name=via" json:"via,omitempty"` StreamSettings *v2ray_core_transport_internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings" json:"stream_settings,omitempty"` ProxySettings *v2ray_core_transport_internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"` } -func (m *StreamSenderConfig) Reset() { *m = StreamSenderConfig{} } -func (m *StreamSenderConfig) String() string { return proto.CompactTextString(m) } -func (*StreamSenderConfig) ProtoMessage() {} -func (*StreamSenderConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } +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 (m *StreamSenderConfig) GetVia() *v2ray_core_common_net.IPOrDomain { +func (m *SenderConfig) GetVia() *v2ray_core_common_net.IPOrDomain { if m != nil { return m.Via } return nil } -func (m *StreamSenderConfig) GetStreamSettings() *v2ray_core_transport_internet.StreamConfig { +func (m *SenderConfig) GetStreamSettings() *v2ray_core_transport_internet.StreamConfig { if m != nil { return m.StreamSettings } return nil } -func (m *StreamSenderConfig) GetProxySettings() *v2ray_core_transport_internet.ProxyConfig { - if m != nil { - return m.ProxySettings - } - return nil -} - -type DatagramSenderConfig struct { - // Send traffic through the given IP. Only IP is allowed. - Via *v2ray_core_common_net.IPOrDomain `protobuf:"bytes,1,opt,name=via" json:"via,omitempty"` - ProxySettings *v2ray_core_transport_internet.ProxyConfig `protobuf:"bytes,2,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"` -} - -func (m *DatagramSenderConfig) Reset() { *m = DatagramSenderConfig{} } -func (m *DatagramSenderConfig) String() string { return proto.CompactTextString(m) } -func (*DatagramSenderConfig) ProtoMessage() {} -func (*DatagramSenderConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } - -func (m *DatagramSenderConfig) GetVia() *v2ray_core_common_net.IPOrDomain { - if m != nil { - return m.Via - } - return nil -} - -func (m *DatagramSenderConfig) GetProxySettings() *v2ray_core_transport_internet.ProxyConfig { +func (m *SenderConfig) GetProxySettings() *v2ray_core_transport_internet.ProxyConfig { if m != nil { return m.ProxySettings } @@ -305,15 +264,17 @@ func (m *DatagramSenderConfig) GetProxySettings() *v2ray_core_transport_internet } type OutboundHandlerConfig struct { - Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` - SenderSettings []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,rep,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"` + 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{8} } +func (*OutboundHandlerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } func (m *OutboundHandlerConfig) GetTag() string { if m != nil { @@ -322,7 +283,7 @@ func (m *OutboundHandlerConfig) GetTag() string { return "" } -func (m *OutboundHandlerConfig) GetSenderSettings() []*v2ray_core_common_serial.TypedMessage { +func (m *OutboundHandlerConfig) GetSenderSettings() *v2ray_core_common_serial.TypedMessage { if m != nil { return m.SenderSettings } @@ -336,17 +297,29 @@ func (m *OutboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.Typ 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((*InboundConfig)(nil), "v2ray.core.app.proxyman.InboundConfig") proto.RegisterType((*AllocationStrategy)(nil), "v2ray.core.app.proxyman.AllocationStrategy") proto.RegisterType((*AllocationStrategy_AllocationStrategyConcurrency)(nil), "v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency") proto.RegisterType((*AllocationStrategy_AllocationStrategyRefresh)(nil), "v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefresh") - proto.RegisterType((*StreamReceiverConfig)(nil), "v2ray.core.app.proxyman.StreamReceiverConfig") - proto.RegisterType((*DatagramReceiverConfig)(nil), "v2ray.core.app.proxyman.DatagramReceiverConfig") + 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((*StreamSenderConfig)(nil), "v2ray.core.app.proxyman.StreamSenderConfig") - proto.RegisterType((*DatagramSenderConfig)(nil), "v2ray.core.app.proxyman.DatagramSenderConfig") + proto.RegisterType((*SenderConfig)(nil), "v2ray.core.app.proxyman.SenderConfig") proto.RegisterType((*OutboundHandlerConfig)(nil), "v2ray.core.app.proxyman.OutboundHandlerConfig") proto.RegisterEnum("v2ray.core.app.proxyman.AllocationStrategy_Type", AllocationStrategy_Type_name, AllocationStrategy_Type_value) } @@ -354,48 +327,52 @@ func init() { func init() { proto.RegisterFile("v2ray.com/core/app/proxyman/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 683 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe4, 0x55, 0xdd, 0x6e, 0xd3, 0x30, - 0x18, 0x25, 0xe9, 0x18, 0xdb, 0x57, 0xd6, 0x15, 0x53, 0x58, 0x29, 0x42, 0x2a, 0x15, 0x82, 0x0a, - 0x50, 0x32, 0x3a, 0x71, 0xc1, 0x15, 0xda, 0x9f, 0xc4, 0x2e, 0xc6, 0x8a, 0x3b, 0x71, 0x81, 0x90, - 0x2a, 0x2f, 0xf1, 0x42, 0x44, 0x62, 0x47, 0xb6, 0x5b, 0x96, 0x97, 0xe1, 0x01, 0x78, 0x0a, 0xae, - 0x90, 0x90, 0x78, 0x9a, 0x3d, 0x01, 0x72, 0x9c, 0x74, 0xd5, 0xda, 0xb2, 0x8d, 0x8a, 0x2b, 0xee, - 0x5c, 0xf7, 0x3b, 0xc7, 0x3e, 0xe7, 0x7c, 0xfe, 0x02, 0xed, 0x61, 0x47, 0x90, 0xd4, 0xf1, 0x78, - 0xec, 0x7a, 0x5c, 0x50, 0x97, 0x24, 0x89, 0x9b, 0x08, 0x7e, 0x92, 0xc6, 0x84, 0xb9, 0x1e, 0x67, - 0xc7, 0x61, 0xe0, 0x24, 0x82, 0x2b, 0x8e, 0xd6, 0x8a, 0x4a, 0x41, 0x1d, 0x92, 0x24, 0x4e, 0x51, - 0xd5, 0x58, 0x3f, 0x47, 0xe1, 0xf1, 0x38, 0xe6, 0xcc, 0x95, 0x54, 0x84, 0x24, 0x72, 0x55, 0x9a, - 0x50, 0xbf, 0x1f, 0x53, 0x29, 0x49, 0x40, 0x0d, 0x55, 0xe3, 0xc9, 0x74, 0x04, 0xa3, 0xca, 0x25, - 0xbe, 0x2f, 0xa8, 0x94, 0x79, 0xe1, 0xa3, 0xd9, 0x85, 0x09, 0x17, 0x2a, 0xaf, 0x72, 0xce, 0x55, - 0x29, 0x41, 0x98, 0xd4, 0xff, 0xbb, 0x21, 0x53, 0x54, 0xe8, 0xea, 0x71, 0x25, 0xad, 0x55, 0x58, - 0xd9, 0x63, 0x47, 0x7c, 0xc0, 0xfc, 0xed, 0x6c, 0xbb, 0xf5, 0xbd, 0x04, 0x68, 0x33, 0x8a, 0xb8, - 0x47, 0x54, 0xc8, 0x59, 0x4f, 0x09, 0xa2, 0x68, 0x90, 0xa2, 0x1d, 0x58, 0xd0, 0xb7, 0xaf, 0x5b, - 0x4d, 0xab, 0x5d, 0xe9, 0xac, 0x3b, 0x33, 0x0c, 0x70, 0x26, 0xa1, 0xce, 0x61, 0x9a, 0x50, 0x9c, - 0xa1, 0xd1, 0x67, 0x28, 0x7b, 0x9c, 0x79, 0x03, 0x21, 0x28, 0xf3, 0xd2, 0xba, 0xdd, 0xb4, 0xda, - 0xe5, 0xce, 0xde, 0x55, 0xc8, 0x26, 0xb7, 0xb6, 0xcf, 0x08, 0xf1, 0x38, 0x3b, 0xea, 0xc3, 0x0d, - 0x41, 0x8f, 0x05, 0x95, 0x9f, 0xea, 0xa5, 0xec, 0xa0, 0xdd, 0xf9, 0x0e, 0xc2, 0x86, 0x0c, 0x17, - 0xac, 0x8d, 0x97, 0xf0, 0xe0, 0x8f, 0xd7, 0x41, 0x35, 0xb8, 0x3e, 0x24, 0xd1, 0xc0, 0xb8, 0xb6, - 0x82, 0xcd, 0x8f, 0xc6, 0x0b, 0xb8, 0x37, 0x93, 0x7c, 0x3a, 0xa4, 0xf5, 0x1c, 0x16, 0xb4, 0x8b, - 0x08, 0x60, 0x71, 0x33, 0xfa, 0x42, 0x52, 0x59, 0xbd, 0xa6, 0xd7, 0x98, 0x30, 0x9f, 0xc7, 0x55, - 0x0b, 0xdd, 0x84, 0xa5, 0xdd, 0x13, 0x1d, 0x2f, 0x89, 0xaa, 0x76, 0xeb, 0x87, 0x0d, 0xb5, 0x9e, - 0x12, 0x94, 0xc4, 0x98, 0x7a, 0x34, 0x1c, 0x52, 0x61, 0xb2, 0x45, 0xaf, 0x01, 0x74, 0x2b, 0xf4, - 0x05, 0x61, 0x81, 0x39, 0xa1, 0xdc, 0x69, 0x8e, 0x9b, 0x62, 0x7a, 0xca, 0x61, 0x54, 0x39, 0x5d, - 0x2e, 0x14, 0xd6, 0x75, 0x78, 0x39, 0x29, 0x96, 0xe8, 0x15, 0x2c, 0x46, 0xa1, 0x54, 0x94, 0xe5, - 0xd1, 0x3d, 0x9c, 0x01, 0xde, 0xeb, 0x1e, 0x88, 0x1d, 0x1e, 0x93, 0x90, 0xe1, 0x1c, 0x80, 0x3e, - 0xc2, 0x6d, 0x32, 0x52, 0xdd, 0x97, 0xb9, 0xec, 0x3c, 0x99, 0x67, 0x57, 0x48, 0x06, 0x23, 0x32, - 0xd9, 0x9e, 0x87, 0xb0, 0x2a, 0x33, 0xc5, 0x7d, 0x49, 0x95, 0x0a, 0x59, 0x20, 0xeb, 0x0b, 0x93, - 0xcc, 0xa3, 0xc7, 0xe0, 0x14, 0x8f, 0xc1, 0x31, 0x3e, 0x19, 0x7f, 0x70, 0xc5, 0x70, 0xf4, 0x72, - 0x8a, 0xd6, 0xa9, 0x05, 0x77, 0x77, 0x88, 0x22, 0x81, 0xf8, 0x7f, 0xac, 0x6c, 0xfd, 0xb2, 0xa0, - 0x96, 0x8f, 0x84, 0x37, 0x84, 0xf9, 0xd1, 0x48, 0x72, 0x15, 0x4a, 0x8a, 0x04, 0x99, 0xd6, 0x65, - 0xac, 0x97, 0xa8, 0x07, 0xb7, 0x44, 0x6e, 0xcb, 0x99, 0xef, 0x76, 0xb3, 0xd4, 0x2e, 0x77, 0x1e, - 0x4f, 0x91, 0x63, 0xa6, 0x60, 0x36, 0x0f, 0xfc, 0x7d, 0x33, 0x04, 0x71, 0xb5, 0x20, 0x28, 0x4c, - 0x47, 0xfb, 0x50, 0xc9, 0xae, 0x7c, 0xc6, 0x68, 0x84, 0x5d, 0x96, 0x71, 0x25, 0x43, 0x8f, 0x32, - 0xac, 0x42, 0xe5, 0x60, 0xa0, 0xc6, 0x27, 0xdc, 0xa9, 0x05, 0xa8, 0x97, 0x07, 0xcd, 0xfc, 0x91, - 0xbc, 0x0d, 0x28, 0x0d, 0x43, 0x92, 0x47, 0x79, 0x89, 0x34, 0x74, 0xf5, 0xb4, 0xbe, 0xb3, 0xe7, - 0xee, 0x3b, 0xf4, 0x6e, 0x86, 0x05, 0x4f, 0x2f, 0x20, 0xed, 0x6a, 0x50, 0xce, 0x79, 0xce, 0x86, - 0xaf, 0x16, 0xd4, 0x8a, 0x56, 0x9e, 0x5f, 0xf6, 0xe4, 0x05, 0xed, 0x79, 0x2f, 0xf8, 0xd3, 0x82, - 0x3b, 0x45, 0x50, 0x17, 0xf5, 0xdd, 0x01, 0xac, 0xca, 0x4c, 0xc3, 0xdf, 0x76, 0x5d, 0xc5, 0xc0, - 0xff, 0x51, 0xcf, 0x6d, 0xbd, 0x85, 0xfb, 0x1e, 0x8f, 0x67, 0x3d, 0xc4, 0xad, 0xb2, 0x11, 0xd6, - 0xd5, 0x1f, 0xe0, 0x0f, 0x4b, 0xc5, 0xf6, 0x37, 0x7b, 0xed, 0x7d, 0x07, 0x93, 0xd4, 0xd9, 0xd6, - 0x80, 0xcd, 0x24, 0x31, 0x6e, 0xc5, 0x84, 0x1d, 0x2d, 0x66, 0xdf, 0xea, 0x8d, 0xdf, 0x01, 0x00, - 0x00, 0xff, 0xff, 0x18, 0xce, 0x1c, 0x24, 0xa1, 0x08, 0x00, 0x00, + // 737 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x55, 0xd1, 0x6e, 0xdb, 0x36, + 0x14, 0x9d, 0xec, 0xc4, 0x49, 0xae, 0x13, 0xc7, 0xe3, 0xb2, 0xc4, 0xf3, 0x36, 0xc0, 0x33, 0x86, + 0xcd, 0xd8, 0x06, 0x29, 0x73, 0x30, 0x60, 0x7b, 0xda, 0x12, 0x27, 0xc0, 0xf2, 0x90, 0xd9, 0xa3, + 0x83, 0x3e, 0x14, 0x05, 0x04, 0x46, 0x62, 0x5c, 0xa1, 0x12, 0x29, 0x90, 0xb4, 0x13, 0xfd, 0x52, + 0xbf, 0xa1, 0x0f, 0xfd, 0x80, 0x7e, 0x4a, 0xff, 0xa0, 0x2f, 0x05, 0x49, 0xc9, 0x36, 0x62, 0xbb, + 0x69, 0x1a, 0xf4, 0x4d, 0x24, 0xcf, 0x39, 0xe4, 0x3d, 0x87, 0x97, 0x82, 0xce, 0xa4, 0x2b, 0x48, + 0xe6, 0x06, 0x3c, 0xf1, 0x02, 0x2e, 0xa8, 0x47, 0xd2, 0xd4, 0x4b, 0x05, 0xbf, 0xcd, 0x12, 0xc2, + 0xbc, 0x80, 0xb3, 0xeb, 0x68, 0xe4, 0xa6, 0x82, 0x2b, 0x8e, 0x0e, 0x0a, 0xa4, 0xa0, 0x2e, 0x49, + 0x53, 0xb7, 0x40, 0x35, 0x0f, 0xef, 0x48, 0x04, 0x3c, 0x49, 0x38, 0xf3, 0x24, 0x15, 0x11, 0x89, + 0x3d, 0x95, 0xa5, 0x34, 0xf4, 0x13, 0x2a, 0x25, 0x19, 0x51, 0x2b, 0xd5, 0xfc, 0x79, 0x39, 0x83, + 0x51, 0xe5, 0x91, 0x30, 0x14, 0x54, 0xca, 0x1c, 0xf8, 0xe3, 0x6a, 0x60, 0xca, 0x85, 0xca, 0x51, + 0xee, 0x1d, 0x94, 0x12, 0x84, 0x49, 0xbd, 0xee, 0x45, 0x4c, 0x51, 0xa1, 0xd1, 0xf3, 0x95, 0xb4, + 0x77, 0x61, 0xe7, 0x9c, 0x5d, 0xf1, 0x31, 0x0b, 0x7b, 0x66, 0xba, 0xfd, 0xba, 0x0c, 0xe8, 0x38, + 0x8e, 0x79, 0x40, 0x54, 0xc4, 0xd9, 0x50, 0x09, 0xa2, 0xe8, 0x28, 0x43, 0xa7, 0xb0, 0xa6, 0x4f, + 0xdf, 0x70, 0x5a, 0x4e, 0xa7, 0xd6, 0x3d, 0x74, 0x57, 0x18, 0xe0, 0x2e, 0x52, 0xdd, 0xcb, 0x2c, + 0xa5, 0xd8, 0xb0, 0xd1, 0x0b, 0xa8, 0x06, 0x9c, 0x05, 0x63, 0x21, 0x28, 0x0b, 0xb2, 0x46, 0xa9, + 0xe5, 0x74, 0xaa, 0xdd, 0xf3, 0x87, 0x88, 0x2d, 0x4e, 0xf5, 0x66, 0x82, 0x78, 0x5e, 0x1d, 0xf9, + 0xb0, 0x21, 0xe8, 0xb5, 0xa0, 0xf2, 0x79, 0xa3, 0x6c, 0x36, 0x3a, 0x7b, 0xdc, 0x46, 0xd8, 0x8a, + 0xe1, 0x42, 0xb5, 0xf9, 0x07, 0x7c, 0xff, 0xc1, 0xe3, 0xa0, 0x3d, 0x58, 0x9f, 0x90, 0x78, 0x6c, + 0x5d, 0xdb, 0xc1, 0x76, 0xd0, 0xfc, 0x1d, 0xbe, 0x59, 0x29, 0xbe, 0x9c, 0xd2, 0xfe, 0x0d, 0xd6, + 0xb4, 0x8b, 0x08, 0xa0, 0x72, 0x1c, 0xdf, 0x90, 0x4c, 0xd6, 0xbf, 0xd0, 0xdf, 0x98, 0xb0, 0x90, + 0x27, 0x75, 0x07, 0x6d, 0xc3, 0xe6, 0xd9, 0xad, 0x8e, 0x97, 0xc4, 0xf5, 0x52, 0xfb, 0x55, 0x19, + 0x6a, 0x98, 0x06, 0x34, 0x9a, 0x50, 0x61, 0x53, 0x45, 0x7f, 0x03, 0xe8, 0x4b, 0xe0, 0x0b, 0xc2, + 0x46, 0x56, 0xbb, 0xda, 0x6d, 0xcd, 0xdb, 0x61, 0x6f, 0x93, 0xcb, 0xa8, 0x72, 0x07, 0x5c, 0x28, + 0xac, 0x71, 0x78, 0x2b, 0x2d, 0x3e, 0xd1, 0x5f, 0x50, 0x89, 0x23, 0xa9, 0x28, 0xcb, 0x43, 0xfb, + 0x61, 0x05, 0xf9, 0x7c, 0xd0, 0x17, 0xa7, 0x3c, 0x21, 0x11, 0xc3, 0x39, 0x01, 0x3d, 0x83, 0xaf, + 0xc8, 0xb4, 0x5e, 0x5f, 0xe6, 0x05, 0xe7, 0x99, 0xfc, 0xfa, 0x80, 0x4c, 0x30, 0x22, 0x8b, 0x17, + 0xf3, 0x12, 0x76, 0xa5, 0x12, 0x94, 0x24, 0xbe, 0xa4, 0x4a, 0x45, 0x6c, 0x24, 0x1b, 0x6b, 0x8b, + 0xca, 0xd3, 0x36, 0x70, 0x8b, 0x36, 0x70, 0x87, 0x86, 0x65, 0xfd, 0xc1, 0x35, 0xab, 0x31, 0xcc, + 0x25, 0xd0, 0x3f, 0xf0, 0x9d, 0xb0, 0x0e, 0xfa, 0x5c, 0x44, 0xa3, 0x88, 0x91, 0xd8, 0x0f, 0xa9, + 0x54, 0x11, 0x33, 0xbb, 0x37, 0xd6, 0x5b, 0x4e, 0x67, 0x13, 0x37, 0x73, 0x4c, 0x3f, 0x87, 0x9c, + 0xce, 0x10, 0xe8, 0x4f, 0x68, 0xe8, 0xd3, 0xde, 0xf8, 0x29, 0x91, 0x52, 0xeb, 0x04, 0x9c, 0x31, + 0x1a, 0x18, 0x76, 0xc5, 0xb0, 0xf7, 0xcd, 0xfa, 0xc0, 0x2e, 0xf7, 0xa6, 0xab, 0xed, 0x37, 0x0e, + 0xec, 0xe5, 0x3d, 0xf9, 0x2f, 0x61, 0x61, 0x3c, 0x0d, 0xb1, 0x0e, 0x65, 0x45, 0x46, 0x26, 0xbd, + 0x2d, 0xac, 0x3f, 0xd1, 0x10, 0xbe, 0xcc, 0x8f, 0x20, 0x66, 0xe5, 0xdb, 0x80, 0x7e, 0x5a, 0x12, + 0x90, 0x7d, 0x86, 0x4c, 0x43, 0x86, 0x17, 0xf6, 0x15, 0xc2, 0xf5, 0x42, 0x60, 0x5a, 0xfb, 0x05, + 0xd4, 0x4c, 0x08, 0x33, 0xc5, 0xf2, 0x83, 0x14, 0x77, 0x0c, 0xbb, 0x90, 0x6b, 0xd7, 0xa1, 0xd6, + 0x1f, 0xab, 0xf9, 0x27, 0xe6, 0xad, 0x03, 0xdb, 0x43, 0xca, 0xc2, 0x69, 0x61, 0x47, 0x50, 0x9e, + 0x44, 0x24, 0xbf, 0x96, 0x1f, 0x71, 0xb3, 0x34, 0x7a, 0x59, 0xf0, 0xa5, 0xc7, 0x07, 0xff, 0xff, + 0x8a, 0xe2, 0x7f, 0xb9, 0x47, 0x74, 0xa0, 0x49, 0xb9, 0xe6, 0x1d, 0x03, 0xde, 0x39, 0xf0, 0x75, + 0xe1, 0xc0, 0x7d, 0x81, 0xf6, 0x61, 0x57, 0x1a, 0x67, 0x3e, 0x35, 0xce, 0x9a, 0xa5, 0x7f, 0xa6, + 0x30, 0xd1, 0x3e, 0x54, 0xe8, 0x6d, 0x1a, 0x09, 0x6a, 0x9a, 0xac, 0x8c, 0xf3, 0x11, 0x6a, 0xc0, + 0x86, 0x16, 0xa1, 0x4c, 0x99, 0xd6, 0xd8, 0xc2, 0xc5, 0xf0, 0xe4, 0x3f, 0xf8, 0x36, 0xe0, 0xc9, + 0xaa, 0x2e, 0x3f, 0xa9, 0x5a, 0x2b, 0x06, 0xfa, 0x67, 0xf4, 0x74, 0xb3, 0x98, 0x7e, 0x59, 0x3a, + 0x78, 0xd2, 0xc5, 0x24, 0x73, 0x7b, 0x9a, 0x70, 0x9c, 0xa6, 0xd6, 0xdf, 0x84, 0xb0, 0xab, 0x8a, + 0xf9, 0x6f, 0x1d, 0xbd, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x26, 0xa3, 0xe8, 0x2c, 0xad, 0x07, 0x00, + 0x00, } diff --git a/app/proxyman/config.proto b/app/proxyman/config.proto index 01862dcc1..1ecc3b850 100644 --- a/app/proxyman/config.proto +++ b/app/proxyman/config.proto @@ -46,22 +46,18 @@ message AllocationStrategy { AllocationStrategyRefresh refresh = 3; } -message StreamReceiverConfig { +message ReceiverConfig { v2ray.core.common.net.PortRange port_range = 1; v2ray.core.common.net.IPOrDomain listen = 2; AllocationStrategy allocation_strategy = 3; v2ray.core.transport.internet.StreamConfig stream_settings = 4; -} - -message DatagramReceiverConfig { - v2ray.core.common.net.PortRange port_range = 1; - v2ray.core.common.net.IPOrDomain listen = 2; - AllocationStrategy allocation_strategy = 3; + bool receive_original_destination = 5; + bool allow_passive_connection = 6; } message InboundHandlerConfig { string tag = 1; - repeated v2ray.core.common.serial.TypedMessage receiver_settings = 2; + v2ray.core.common.serial.TypedMessage receiver_settings = 2; v2ray.core.common.serial.TypedMessage proxy_settings = 3; } @@ -69,21 +65,17 @@ message OutboundConfig { } -message StreamSenderConfig { +message SenderConfig { // Send traffic through the given IP. Only IP is allowed. v2ray.core.common.net.IPOrDomain via = 1; v2ray.core.transport.internet.StreamConfig stream_settings = 2; v2ray.core.transport.internet.ProxyConfig proxy_settings = 3; } -message DatagramSenderConfig { - // Send traffic through the given IP. Only IP is allowed. - v2ray.core.common.net.IPOrDomain via = 1; - v2ray.core.transport.internet.ProxyConfig proxy_settings = 2; -} - message OutboundHandlerConfig { string tag = 1; - repeated v2ray.core.common.serial.TypedMessage sender_settings = 2; + v2ray.core.common.serial.TypedMessage sender_settings = 2; v2ray.core.common.serial.TypedMessage proxy_settings = 3; + int64 expire = 4; + string comment = 5; } diff --git a/app/proxyman/inbound/always.go b/app/proxyman/inbound/always.go new file mode 100644 index 000000000..8eace8529 --- /dev/null +++ b/app/proxyman/inbound/always.go @@ -0,0 +1,78 @@ +package inbound + +import ( + "context" + + "v2ray.com/core/app/proxyman" + "v2ray.com/core/common/dice" + "v2ray.com/core/common/log" + "v2ray.com/core/common/net" + "v2ray.com/core/proxy" +) + +type AlwaysOnInboundHandler struct { + proxy proxy.InboundHandler + workers []worker +} + +func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) { + p, err := proxy.CreateInboundHandler(ctx, proxyConfig) + if err != nil { + return nil, err + } + + h := &AlwaysOnInboundHandler{ + proxy: p, + } + + nl := p.Network() + pr := receiverConfig.PortRange + for port := pr.From; port <= pr.To; port++ { + if nl.HasNetwork(net.Network_TCP) { + log.Debug("Proxyman|DefaultInboundHandler: creating tcp worker on ", receiverConfig.Listen.AsAddress(), ":", port) + worker := &tcpWorker{ + address: receiverConfig.Listen.AsAddress(), + port: net.Port(port), + proxy: p, + stream: receiverConfig.StreamSettings, + recvOrigDest: receiverConfig.ReceiveOriginalDestination, + tag: tag, + allowPassiveConn: receiverConfig.AllowPassiveConnection, + } + h.workers = append(h.workers, worker) + } + + if nl.HasNetwork(net.Network_UDP) { + worker := &udpWorker{ + tag: tag, + proxy: p, + address: receiverConfig.Listen.AsAddress(), + port: net.Port(port), + recvOrigDest: receiverConfig.ReceiveOriginalDestination, + } + h.workers = append(h.workers, worker) + } + } + + return h, nil +} + +func (h *AlwaysOnInboundHandler) Start() error { + for _, worker := range h.workers { + if err := worker.Start(); err != nil { + return err + } + } + return nil +} + +func (h *AlwaysOnInboundHandler) Close() { + for _, worker := range h.workers { + worker.Close() + } +} + +func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (proxy.InboundHandler, net.Port, int) { + w := h.workers[dice.Roll(len(h.workers))] + return w.Proxy(), w.Port(), 9999 +} diff --git a/app/proxyman/inbound/dynamic.go b/app/proxyman/inbound/dynamic.go new file mode 100644 index 000000000..64c913968 --- /dev/null +++ b/app/proxyman/inbound/dynamic.go @@ -0,0 +1,143 @@ +package inbound + +import ( + "context" + "sync" + "time" + + "v2ray.com/core/app/proxyman" + "v2ray.com/core/common/dice" + "v2ray.com/core/common/log" + v2net "v2ray.com/core/common/net" + "v2ray.com/core/proxy" +) + +type DynamicInboundHandler struct { + sync.Mutex + tag string + ctx context.Context + cancel context.CancelFunc + proxyConfig interface{} + receiverConfig *proxyman.ReceiverConfig + portsInUse map[v2net.Port]bool + worker []worker + worker2Recycle []worker + lastRefresh time.Time +} + +func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) { + ctx, cancel := context.WithCancel(ctx) + h := &DynamicInboundHandler{ + ctx: ctx, + tag: tag, + cancel: cancel, + proxyConfig: proxyConfig, + receiverConfig: receiverConfig, + portsInUse: make(map[v2net.Port]bool), + } + + return h, nil +} + +func (h *DynamicInboundHandler) allocatePort() v2net.Port { + from := int(h.receiverConfig.PortRange.From) + delta := int(h.receiverConfig.PortRange.To) - from + 1 + h.Lock() + defer h.Unlock() + + for { + r := dice.Roll(delta) + port := v2net.Port(from + r) + _, used := h.portsInUse[port] + if !used { + h.portsInUse[port] = true + return port + } + } +} + +func (h *DynamicInboundHandler) refresh() error { + h.lastRefresh = time.Now() + + ports2Del := make([]v2net.Port, 0, 16) + for _, worker := range h.worker2Recycle { + worker.Close() + ports2Del = append(ports2Del, worker.Port()) + } + + h.Lock() + for _, port := range ports2Del { + delete(h.portsInUse, port) + } + h.Unlock() + + h.worker2Recycle, h.worker = h.worker, h.worker2Recycle[:0] + + for i := uint32(0); i < h.receiverConfig.AllocationStrategy.GetConcurrencyValue(); i++ { + port := h.allocatePort() + p, err := proxy.CreateInboundHandler(h.ctx, h.proxyConfig) + if err != nil { + log.Warning("Proxyman|DefaultInboundHandler: Failed to create proxy instance: ", err) + continue + } + nl := p.Network() + if nl.HasNetwork(v2net.Network_TCP) { + worker := &tcpWorker{ + tag: h.tag, + address: h.receiverConfig.Listen.AsAddress(), + port: port, + proxy: p, + stream: h.receiverConfig.StreamSettings, + recvOrigDest: h.receiverConfig.ReceiveOriginalDestination, + allowPassiveConn: h.receiverConfig.AllowPassiveConnection, + } + if err := worker.Start(); err != nil { + return err + } + h.worker = append(h.worker, worker) + } + + if nl.HasNetwork(v2net.Network_UDP) { + worker := &udpWorker{ + tag: h.tag, + proxy: p, + address: h.receiverConfig.Listen.AsAddress(), + port: port, + recvOrigDest: h.receiverConfig.ReceiveOriginalDestination, + } + if err := worker.Start(); err != nil { + return err + } + h.worker = append(h.worker, worker) + } + } + + return nil +} + +func (h *DynamicInboundHandler) monitor() { + for { + select { + case <-h.ctx.Done(): + return + case <-time.After(time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue())): + h.refresh() + } + } +} + +func (h *DynamicInboundHandler) Start() error { + err := h.refresh() + go h.monitor() + return err +} + +func (h *DynamicInboundHandler) Close() { + h.cancel() +} + +func (h *DynamicInboundHandler) GetRandomInboundProxy() (proxy.InboundHandler, v2net.Port, int) { + w := h.worker[dice.Roll(len(h.worker))] + expire := h.receiverConfig.AllocationStrategy.GetRefreshValue() - uint32(time.Since(h.lastRefresh)/time.Minute) + return w.Proxy(), w.Port(), int(expire) +} diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go new file mode 100644 index 000000000..8bec78ae2 --- /dev/null +++ b/app/proxyman/inbound/inbound.go @@ -0,0 +1,94 @@ +package inbound + +import ( + "context" + + "v2ray.com/core/app/proxyman" + "v2ray.com/core/common" + "v2ray.com/core/common/errors" +) + +type DefaultInboundHandlerManager struct { + handlers []proxyman.InboundHandler + taggedHandlers map[string]proxyman.InboundHandler +} + +func New(ctx context.Context, config *proxyman.InboundConfig) (*DefaultInboundHandlerManager, error) { + return &DefaultInboundHandlerManager{ + taggedHandlers: make(map[string]proxyman.InboundHandler), + }, nil +} + +func (m *DefaultInboundHandlerManager) 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 errors.New("Proxyman|DefaultInboundHandlerManager: Not a ReceiverConfig.") + } + 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 + } + + if handler == nil { + return errors.New("Proxyman|DefaultInboundHandlerManager: Unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type) + } + + m.handlers = append(m.handlers, handler) + if len(tag) > 0 { + m.taggedHandlers[tag] = handler + } + return nil +} + +func (m *DefaultInboundHandlerManager) GetHandler(ctx context.Context, tag string) (proxyman.InboundHandler, error) { + handler, found := m.taggedHandlers[tag] + if !found { + return nil, errors.New("Proxymand|DefaultInboundHandlerManager: Handler not found: ", tag) + } + return handler, nil +} + +func (m *DefaultInboundHandlerManager) Start() error { + for _, handler := range m.handlers { + if err := handler.Start(); err != nil { + return err + } + } + return nil +} + +func (m *DefaultInboundHandlerManager) Close() { + for _, handler := range m.handlers { + handler.Close() + } +} + +func (m *DefaultInboundHandlerManager) Interface() interface{} { + return (*proxyman.InboundHandlerManager)(nil) +} + +func init() { + common.Must(common.RegisterConfig((*proxyman.InboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return New(ctx, config.(*proxyman.InboundConfig)) + })) +} diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go new file mode 100644 index 000000000..1820554ae --- /dev/null +++ b/app/proxyman/inbound/worker.go @@ -0,0 +1,269 @@ +package inbound + +import ( + "context" + "io" + "net" + "sync" + "sync/atomic" + "time" + + "v2ray.com/core/common/buf" + "v2ray.com/core/common/log" + v2net "v2ray.com/core/common/net" + "v2ray.com/core/proxy" + "v2ray.com/core/transport/internet" + "v2ray.com/core/transport/internet/tcp" + "v2ray.com/core/transport/internet/udp" +) + +type worker interface { + Start() error + Close() + Port() v2net.Port + Proxy() proxy.InboundHandler +} + +type tcpWorker struct { + address v2net.Address + port v2net.Port + proxy proxy.InboundHandler + stream *internet.StreamConfig + recvOrigDest bool + tag string + allowPassiveConn bool + + ctx context.Context + cancel context.CancelFunc + hub *internet.TCPHub +} + +func (w *tcpWorker) callback(conn internet.Connection) { + ctx, cancel := context.WithCancel(w.ctx) + if w.recvOrigDest { + dest := tcp.GetOriginalDestination(conn) + if dest.IsValid() { + ctx = proxy.ContextWithOriginalDestination(ctx, dest) + } + } + if len(w.tag) > 0 { + ctx = proxy.ContextWithInboundTag(ctx, w.tag) + } + ctx = proxy.ContextWithAllowPassiveConnection(ctx, w.allowPassiveConn) + ctx = proxy.ContextWithInboundDestination(ctx, v2net.TCPDestination(w.address, w.port)) + w.proxy.Process(ctx, v2net.Network_TCP, conn) + cancel() + conn.Close() +} + +func (w *tcpWorker) Proxy() proxy.InboundHandler { + return w.proxy +} + +func (w *tcpWorker) Start() error { + ctx, cancel := context.WithCancel(context.Background()) + w.ctx = ctx + w.cancel = cancel + hub, err := internet.ListenTCP(w.address, w.port, w.callback, w.stream) + if err != nil { + return err + } + w.hub = hub + return nil +} + +func (w *tcpWorker) Close() { + log.Debug("Proxyman|TCPWorker: Closed. ", w.port) + w.hub.Close() + w.cancel() +} + +func (w *tcpWorker) Port() v2net.Port { + return w.port +} + +type udpConn struct { + cancel context.CancelFunc + lastActivityTime int64 // in seconds + input chan []byte + output func([]byte) (int, error) + closer func() error + remote net.Addr + local net.Addr +} + +func (c *udpConn) updateActivity() { + atomic.StoreInt64(&c.lastActivityTime, time.Now().Unix()) +} + +func (c *udpConn) Read(buf []byte) (int, error) { + in, open := <-c.input + if !open { + return 0, io.EOF + } + c.updateActivity() + return copy(buf, in), nil +} + +func (c *udpConn) Write(buf []byte) (int, error) { + n, err := c.output(buf) + if err == nil { + c.updateActivity() + } + return n, err +} + +func (c *udpConn) Close() error { + close(c.input) + c.cancel() + return nil +} + +func (c *udpConn) RemoteAddr() net.Addr { + return c.remote +} + +func (c *udpConn) LocalAddr() net.Addr { + return c.remote +} + +func (*udpConn) SetDeadline(time.Time) error { + return nil +} + +func (*udpConn) SetReadDeadline(time.Time) error { + return nil +} + +func (*udpConn) SetWriteDeadline(time.Time) error { + return nil +} + +func (*udpConn) Reusable() bool { + return false +} + +func (*udpConn) SetReusable(bool) {} + +type udpWorker struct { + sync.RWMutex + + proxy proxy.InboundHandler + hub *udp.Hub + address v2net.Address + port v2net.Port + recvOrigDest bool + tag string + + ctx context.Context + cancel context.CancelFunc + activeConn map[v2net.Destination]*udpConn +} + +func (w *udpWorker) getConnection(src v2net.Destination) (*udpConn, bool) { + w.Lock() + defer w.Unlock() + + if conn, found := w.activeConn[src]; found { + return conn, true + } + + conn := &udpConn{ + input: make(chan []byte, 32), + output: func(b []byte) (int, error) { + return w.hub.WriteTo(b, src) + }, + closer: func() error { + w.Lock() + delete(w.activeConn, src) + w.Unlock() + return nil + }, + remote: &net.UDPAddr{ + IP: src.Address.IP(), + Port: int(src.Port), + }, + local: &net.UDPAddr{ + IP: w.address.IP(), + Port: int(w.port), + }, + } + + conn.updateActivity() + return conn, false +} + +func (w *udpWorker) callback(b *buf.Buffer, source v2net.Destination, originalDest v2net.Destination) { + conn, existing := w.getConnection(source) + conn.input <- b.Bytes() + + if !existing { + go func() { + ctx := w.ctx + ctx, cancel := context.WithCancel(ctx) + conn.cancel = cancel + if originalDest.IsValid() { + ctx = proxy.ContextWithOriginalDestination(ctx, originalDest) + } + if len(w.tag) > 0 { + ctx = proxy.ContextWithInboundTag(ctx, w.tag) + } + ctx = proxy.ContextWithSource(ctx, source) + ctx = proxy.ContextWithInboundDestination(ctx, v2net.UDPDestination(w.address, w.port)) + w.proxy.Process(ctx, v2net.Network_UDP, conn) + conn.cancel() + }() + } +} + +func (w *udpWorker) removeConn(src v2net.Destination) { + w.Lock() + delete(w.activeConn, src) + w.Unlock() +} + +func (w *udpWorker) Start() error { + ctx, cancel := context.WithCancel(context.Background()) + w.ctx = ctx + w.cancel = cancel + h, err := udp.ListenUDP(w.address, w.port, udp.ListenOption{ + Callback: w.callback, + ReceiveOriginalDest: w.recvOrigDest, + }) + if err != nil { + return err + } + w.hub = h + return nil +} + +func (w *udpWorker) Close() { + w.hub.Close() + w.cancel() +} + +func (w *udpWorker) monitor() { + for { + select { + case <-w.ctx.Done(): + return + case <-time.After(time.Second * 16): + nowSec := time.Now().Unix() + w.Lock() + for addr, conn := range w.activeConn { + if nowSec-conn.lastActivityTime > 8 { + w.removeConn(addr) + conn.Close() + } + } + } + } +} + +func (w *udpWorker) Port() v2net.Port { + return w.port +} + +func (w *udpWorker) Proxy() proxy.InboundHandler { + return w.proxy +} diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index 3beaf55a6..3caca46fc 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -2,39 +2,58 @@ package outbound import ( "context" + "errors" + "io" + "net" + "time" + "v2ray.com/core/app" "v2ray.com/core/app/proxyman" - "v2ray.com/core/common/errors" - "v2ray.com/core/common/net" + "v2ray.com/core/common/buf" + "v2ray.com/core/common/log" + v2net "v2ray.com/core/common/net" "v2ray.com/core/proxy" "v2ray.com/core/transport/internet" + "v2ray.com/core/transport/ray" ) type Handler struct { - config *proxyman.OutboundHandlerConfig - streamSettings *proxyman.StreamSenderConfig - datagramSettings *proxyman.DatagramSenderConfig - proxy proxy.OutboundHandler + config *proxyman.OutboundHandlerConfig + senderSettings *proxyman.SenderConfig + proxy proxy.OutboundHandler + outboundManager proxyman.OutboundHandlerManager } func NewHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) (*Handler, error) { h := &Handler{ config: config, } - for _, rawSettings := range config.SenderSettings { - settings, err := rawSettings.GetInstance() + space := app.SpaceFromContext(ctx) + if space == nil { + return nil, errors.New("Proxyman|OutboundHandler: No space in context.") + } + space.OnInitialize(func() error { + ohm := proxyman.OutboundHandlerManagerFromSpace(space) + if ohm == nil { + return errors.New("Proxyman|OutboundHandler: No OutboundManager in space.") + } + h.outboundManager = ohm + return nil + }) + + if config.SenderSettings != nil { + senderSettings, err := config.SenderSettings.GetInstance() if err != nil { return nil, err } - switch ts := settings.(type) { - case *proxyman.StreamSenderConfig: - h.streamSettings = ts - case *proxyman.DatagramSenderConfig: - h.datagramSettings = ts + switch s := senderSettings.(type) { + case *proxyman.SenderConfig: + h.senderSettings = s default: - return nil, errors.New("Proxyman|DefaultOutboundHandler: Unknown sender settings: ", rawSettings.Type) + return nil, errors.New("Proxyman|DefaultOutboundHandler: settings is not SenderConfig.") } } + proxyHandler, err := config.GetProxyHandler(proxy.ContextWithDialer(ctx, h)) if err != nil { return nil, err @@ -44,38 +63,114 @@ func NewHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) (*H return h, nil } -func (h *Handler) Dial(ctx context.Context, destination net.Destination) (internet.Connection, error) { - switch destination.Network { - case net.Network_TCP: - return h.dialStream(ctx, destination) - case net.Network_UDP: - return h.dialDatagram(ctx, destination) - default: - panic("Proxyman|DefaultOutboundHandler: unexpected network.") +func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) { + ctx = proxy.ContextWithDialer(ctx, h) + h.proxy.Process(ctx, outboundRay) +} + +func (h *Handler) Dial(ctx context.Context, dest v2net.Destination) (internet.Connection, error) { + if h.senderSettings != nil { + if h.senderSettings.ProxySettings.HasTag() { + tag := h.senderSettings.ProxySettings.Tag + handler := h.outboundManager.GetHandler(tag) + if handler != nil { + log.Info("Proxyman|OutboundHandler: Proxying to ", dest) + ctx = proxy.ContextWithDestination(ctx, dest) + stream := ray.NewRay(ctx) + go handler.Dispatch(ctx, stream) + return NewConnection(stream), nil + } + + log.Warning("Proxyman|OutboundHandler: Failed to get outbound handler with tag: ", tag) + } + + if h.senderSettings.Via != nil { + ctx = internet.ContextWithDialerSource(ctx, h.senderSettings.Via.AsAddress()) + } + if h.senderSettings != nil { + ctx = internet.ContextWithStreamSettings(ctx, h.senderSettings.StreamSettings) + } + } + + return internet.Dial(ctx, dest) +} + +type Connection struct { + stream ray.Ray + closed bool + localAddr net.Addr + remoteAddr net.Addr + + reader *buf.BufferToBytesReader + writer *buf.BytesToBufferWriter +} + +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.NewBytesReader(stream.InboundOutput()), + writer: buf.NewBytesWriter(stream.InboundInput()), } } -func (h *Handler) dialStream(ctx context.Context, destination net.Destination) (internet.Connection, error) { - var src net.Address - if h.streamSettings != nil { - src = h.streamSettings.Via.AsAddress() +// Read implements net.Conn.Read(). +func (v *Connection) Read(b []byte) (int, error) { + if v.closed { + return 0, io.EOF } - var options internet.DialerOptions - if h.streamSettings != nil { - options.Proxy = h.streamSettings.ProxySettings - options.Stream = h.streamSettings.StreamSettings - } - return internet.Dial(src, destination, options) + return v.reader.Read(b) } -func (h *Handler) dialDatagram(ctx context.Context, destination net.Destination) (internet.Connection, error) { - var src net.Address - if h.datagramSettings != nil { - src = h.datagramSettings.Via.AsAddress() +// Write implements net.Conn.Write(). +func (v *Connection) Write(b []byte) (int, error) { + if v.closed { + return 0, io.ErrClosedPipe } - var options internet.DialerOptions - if h.datagramSettings != nil { - options.Proxy = h.datagramSettings.ProxySettings - } - return internet.Dial(src, destination, options) + return v.writer.Write(b) +} + +// Close implements net.Conn.Close(). +func (v *Connection) Close() error { + v.closed = true + v.stream.InboundInput().Close() + v.stream.InboundOutput().CloseError() + 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 +} + +func (v *Connection) SetDeadline(t time.Time) error { + return nil +} + +func (v *Connection) SetReadDeadline(t time.Time) error { + return nil +} + +func (v *Connection) SetWriteDeadline(t time.Time) error { + return nil +} + +func (v *Connection) Reusable() bool { + return false +} + +func (v *Connection) SetReusable(bool) { + } diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 8d2e38ee3..aa12f5f0b 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -6,18 +6,17 @@ import ( "v2ray.com/core/app/proxyman" "v2ray.com/core/common" - "v2ray.com/core/proxy" ) type DefaultOutboundHandlerManager struct { sync.RWMutex - defaultHandler proxy.OutboundHandler - taggedHandler map[string]proxy.OutboundHandler + defaultHandler *Handler + taggedHandler map[string]*Handler } func New(ctx context.Context, config *proxyman.OutboundConfig) (*DefaultOutboundHandlerManager, error) { return &DefaultOutboundHandlerManager{ - taggedHandler: make(map[string]proxy.OutboundHandler), + taggedHandler: make(map[string]*Handler), }, nil } @@ -25,7 +24,7 @@ func (DefaultOutboundHandlerManager) Interface() interface{} { return (*proxyman.OutboundHandlerManager)(nil) } -func (v *DefaultOutboundHandlerManager) GetDefaultHandler() proxy.OutboundHandler { +func (v *DefaultOutboundHandlerManager) GetDefaultHandler() proxyman.OutboundHandler { v.RLock() defer v.RUnlock() if v.defaultHandler == nil { @@ -34,14 +33,7 @@ func (v *DefaultOutboundHandlerManager) GetDefaultHandler() proxy.OutboundHandle return v.defaultHandler } -func (v *DefaultOutboundHandlerManager) SetDefaultHandler(handler proxy.OutboundHandler) error { - v.Lock() - defer v.Unlock() - v.defaultHandler = handler - return nil -} - -func (v *DefaultOutboundHandlerManager) GetHandler(tag string) proxy.OutboundHandler { +func (v *DefaultOutboundHandlerManager) GetHandler(tag string) proxyman.OutboundHandler { v.RLock() defer v.RUnlock() if handler, found := v.taggedHandler[tag]; found { @@ -50,11 +42,22 @@ func (v *DefaultOutboundHandlerManager) GetHandler(tag string) proxy.OutboundHan return nil } -func (v *DefaultOutboundHandlerManager) SetHandler(tag string, handler proxy.OutboundHandler) error { +func (v *DefaultOutboundHandlerManager) AddHandler(ctx context.Context, config *proxyman.OutboundHandlerConfig) error { v.Lock() defer v.Unlock() - v.taggedHandler[tag] = handler + handler, err := NewHandler(ctx, config) + if err != nil { + return err + } + if v.defaultHandler == nil { + v.defaultHandler = handler + } + + if len(config.Tag) > 0 { + v.taggedHandler[config.Tag] = handler + } + return nil } diff --git a/app/proxyman/proxyman.go b/app/proxyman/proxyman.go index 63b87dae8..f6dde91d0 100644 --- a/app/proxyman/proxyman.go +++ b/app/proxyman/proxyman.go @@ -11,21 +11,28 @@ import ( ) type InboundHandlerManager interface { - GetHandler(tag string) (proxy.InboundHandler, int) + GetHandler(ctx context.Context, tag string) (InboundHandler, error) + AddHandler(ctx context.Context, config *InboundHandlerConfig) error + Start() error + Close() } type InboundHandler interface { + Start() error + Close() + + // For migration + GetRandomInboundProxy() (proxy.InboundHandler, net.Port, int) } type OutboundHandlerManager interface { - GetHandler(tag string) proxy.OutboundHandler - GetDefaultHandler() proxy.OutboundHandler - SetDefaultHandler(handler proxy.OutboundHandler) error - SetHandler(tag string, handler proxy.OutboundHandler) error + GetHandler(tag string) OutboundHandler + GetDefaultHandler() OutboundHandler + AddHandler(ctx context.Context, config *OutboundHandlerConfig) error } type OutboundHandler interface { - Dispatch(ctx context.Context, destination net.Destination, outboundRay ray.OutboundRay) + Dispatch(ctx context.Context, outboundRay ray.OutboundRay) } func InboundHandlerManagerFromSpace(space app.Space) InboundHandlerManager { diff --git a/app/router/condition.go b/app/router/condition.go index 637322b4b..ea169f498 100644 --- a/app/router/condition.go +++ b/app/router/condition.go @@ -1,16 +1,18 @@ package router import ( + "context" "net" "regexp" "strings" v2net "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" "v2ray.com/core/proxy" ) type Condition interface { - Apply(session *proxy.SessionInfo) bool + Apply(ctx context.Context) bool } type ConditionChan []Condition @@ -25,9 +27,9 @@ func (v *ConditionChan) Add(cond Condition) *ConditionChan { return v } -func (v *ConditionChan) Apply(session *proxy.SessionInfo) bool { +func (v *ConditionChan) Apply(ctx context.Context) bool { for _, cond := range *v { - if !cond.Apply(session) { + if !cond.Apply(ctx) { return false } } @@ -50,9 +52,9 @@ func (v *AnyCondition) Add(cond Condition) *AnyCondition { return v } -func (v *AnyCondition) Apply(session *proxy.SessionInfo) bool { +func (v *AnyCondition) Apply(ctx context.Context) bool { for _, cond := range *v { - if cond.Apply(session) { + if cond.Apply(ctx) { return true } } @@ -73,8 +75,8 @@ func NewPlainDomainMatcher(pattern string) *PlainDomainMatcher { } } -func (v *PlainDomainMatcher) Apply(session *proxy.SessionInfo) bool { - dest := session.Destination +func (v *PlainDomainMatcher) Apply(ctx context.Context) bool { + dest := proxy.DestinationFromContext(ctx) if !dest.Address.Family().IsDomain() { return false } @@ -96,8 +98,8 @@ func NewRegexpDomainMatcher(pattern string) (*RegexpDomainMatcher, error) { }, nil } -func (v *RegexpDomainMatcher) Apply(session *proxy.SessionInfo) bool { - dest := session.Destination +func (v *RegexpDomainMatcher) Apply(ctx context.Context) bool { + dest := proxy.DestinationFromContext(ctx) if !dest.Address.Family().IsDomain() { return false } @@ -121,15 +123,31 @@ func NewCIDRMatcher(ip []byte, mask uint32, onSource bool) (*CIDRMatcher, error) }, nil } -func (v *CIDRMatcher) Apply(session *proxy.SessionInfo) bool { - dest := session.Destination +func (v *CIDRMatcher) Apply(ctx context.Context) bool { + var dest v2net.Destination if v.onSource { - dest = session.Source + dest = proxy.SourceFromContext(ctx) + } else { + dest = proxy.DestinationFromContext(ctx) } + if !dest.Address.Family().Either(v2net.AddressFamilyIPv4, v2net.AddressFamilyIPv6) { return false } - return v.cidr.Contains(dest.Address.IP()) + + ips := []net.IP{dest.Address.IP()} + if resolveIPs, ok := proxy.ResolvedIPsFromContext(ctx); ok { + for _, rip := range resolveIPs { + ips = append(ips, rip.IP()) + } + } + + for _, ip := range ips { + if v.cidr.Contains(ip) { + return true + } + } + return false } type IPv4Matcher struct { @@ -144,15 +162,30 @@ func NewIPv4Matcher(ipnet *v2net.IPNet, onSource bool) *IPv4Matcher { } } -func (v *IPv4Matcher) Apply(session *proxy.SessionInfo) bool { - dest := session.Destination +func (v *IPv4Matcher) Apply(ctx context.Context) bool { + var dest v2net.Destination if v.onSource { - dest = session.Source + dest = proxy.SourceFromContext(ctx) + } else { + dest = proxy.DestinationFromContext(ctx) } if !dest.Address.Family().Either(v2net.AddressFamilyIPv4) { return false } - return v.ipv4net.Contains(dest.Address.IP()) + + ips := []net.IP{dest.Address.IP()} + if resolvedIPs, ok := proxy.ResolvedIPsFromContext(ctx); ok { + for _, rip := range resolvedIPs { + ips = append(ips, rip.IP()) + } + } + + for _, ip := range ips { + if v.ipv4net.Contains(ip) { + return true + } + } + return false } type PortMatcher struct { @@ -165,8 +198,9 @@ func NewPortMatcher(portRange v2net.PortRange) *PortMatcher { } } -func (v *PortMatcher) Apply(session *proxy.SessionInfo) bool { - return v.port.Contains(session.Destination.Port) +func (v *PortMatcher) Apply(ctx context.Context) bool { + dest := proxy.DestinationFromContext(ctx) + return v.port.Contains(dest.Port) } type NetworkMatcher struct { @@ -179,8 +213,9 @@ func NewNetworkMatcher(network *v2net.NetworkList) *NetworkMatcher { } } -func (v *NetworkMatcher) Apply(session *proxy.SessionInfo) bool { - return v.network.HasNetwork(session.Destination.Network) +func (v *NetworkMatcher) Apply(ctx context.Context) bool { + dest := proxy.DestinationFromContext(ctx) + return v.network.HasNetwork(dest.Network) } type UserMatcher struct { @@ -193,12 +228,13 @@ func NewUserMatcher(users []string) *UserMatcher { } } -func (v *UserMatcher) Apply(session *proxy.SessionInfo) bool { - if session.User == nil { +func (v *UserMatcher) Apply(ctx context.Context) bool { + user := protocol.UserFromContext(ctx) + if user == nil { return false } for _, u := range v.user { - if u == session.User.Email { + if u == user.Email { return true } } @@ -215,13 +251,14 @@ func NewInboundTagMatcher(tags []string) *InboundTagMatcher { } } -func (v *InboundTagMatcher) Apply(session *proxy.SessionInfo) bool { - if session.Inbound == nil || len(session.Inbound.Tag) == 0 { +func (v *InboundTagMatcher) Apply(ctx context.Context) bool { + tag := proxy.InboundTagFromContext(ctx) + if len(tag) == 0 { return false } for _, t := range v.tags { - if t == session.Inbound.Tag { + if t == tag { return true } } diff --git a/app/router/config.go b/app/router/config.go index 5f520a441..630d8e6a5 100644 --- a/app/router/config.go +++ b/app/router/config.go @@ -1,11 +1,11 @@ package router import ( + "context" "net" "v2ray.com/core/common/errors" v2net "v2ray.com/core/common/net" - "v2ray.com/core/proxy" ) type Rule struct { @@ -13,8 +13,8 @@ type Rule struct { Condition Condition } -func (v *Rule) Apply(session *proxy.SessionInfo) bool { - return v.Condition.Apply(session) +func (v *Rule) Apply(ctx context.Context) bool { + return v.Condition.Apply(ctx) } func (v *RoutingRule) BuildCondition() (Condition, error) { diff --git a/app/router/router.go b/app/router/router.go index 812880a3d..3e6e5d17c 100644 --- a/app/router/router.go +++ b/app/router/router.go @@ -55,43 +55,34 @@ func NewRouter(ctx context.Context, config *Config) (*Router, error) { } // Private: Visible for testing. -func (v *Router) ResolveIP(dest net.Destination) []net.Destination { +func (v *Router) ResolveIP(dest net.Destination) []net.Address { ips := v.dnsServer.Get(dest.Address.Domain()) if len(ips) == 0 { return nil } - dests := make([]net.Destination, len(ips)) + dests := make([]net.Address, len(ips)) for idx, ip := range ips { - if dest.Network == net.Network_TCP { - dests[idx] = net.TCPDestination(net.IPAddress(ip), dest.Port) - } else { - dests[idx] = net.UDPDestination(net.IPAddress(ip), dest.Port) - } + dests[idx] = net.IPAddress(ip) } return dests } -func (v *Router) takeDetourWithoutCache(session *proxy.SessionInfo) (string, error) { +func (v *Router) takeDetourWithoutCache(ctx context.Context) (string, error) { for _, rule := range v.rules { - if rule.Apply(session) { + if rule.Apply(ctx) { return rule.Tag, nil } } - dest := session.Destination + + dest := proxy.DestinationFromContext(ctx) if v.domainStrategy == Config_IpIfNonMatch && dest.Address.Family().IsDomain() { log.Info("Router: Looking up IP for ", dest) ipDests := v.ResolveIP(dest) if ipDests != nil { - for _, ipDest := range ipDests { - log.Info("Router: Trying IP ", ipDest) - for _, rule := range v.rules { - if rule.Apply(&proxy.SessionInfo{ - Source: session.Source, - Destination: ipDest, - User: session.User, - }) { - return rule.Tag, nil - } + ctx = proxy.ContextWithResolveIPs(ctx, ipDests) + for _, rule := range v.rules { + if rule.Apply(ctx) { + return rule.Tag, nil } } } @@ -100,11 +91,11 @@ func (v *Router) takeDetourWithoutCache(session *proxy.SessionInfo) (string, err return "", ErrNoRuleApplicable } -func (v *Router) TakeDetour(session *proxy.SessionInfo) (string, error) { +func (v *Router) TakeDetour(ctx context.Context) (string, error) { //destStr := dest.String() //found, tag, err := v.cache.Get(destStr) //if !found { - tag, err := v.takeDetourWithoutCache(session) + tag, err := v.takeDetourWithoutCache(ctx) //v.cache.Set(destStr, tag, err) return tag, err //} diff --git a/app/router/router_test.go b/app/router/router_test.go index 629ae628e..8048de41b 100644 --- a/app/router/router_test.go +++ b/app/router/router_test.go @@ -41,7 +41,8 @@ func TestSimpleRouter(t *testing.T) { r := FromSpace(space) - tag, err := r.TakeDetour(&proxy.SessionInfo{Destination: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)}) + ctx = proxy.ContextWithDestination(ctx, net.TCPDestination(net.DomainAddress("v2ray.com"), 80)) + tag, err := r.TakeDetour(ctx) assert.Error(err).IsNil() assert.String(tag).Equals("test") } diff --git a/common/net/destination.go b/common/net/destination.go index 11426e8b1..47087a8d0 100644 --- a/common/net/destination.go +++ b/common/net/destination.go @@ -48,6 +48,10 @@ func (v Destination) String() string { return v.Network.URLPrefix() + ":" + v.NetAddr() } +func (v Destination) IsValid() bool { + return v.Network != Network_Unknown +} + func (v *Endpoint) AsDestination() Destination { return Destination{ Network: v.Network, diff --git a/common/protocol/context.go b/common/protocol/context.go new file mode 100644 index 000000000..c644e1db6 --- /dev/null +++ b/common/protocol/context.go @@ -0,0 +1,23 @@ +package protocol + +import ( + "context" +) + +type key int + +const ( + userKey key = iota +) + +func ContextWithUser(ctx context.Context, user *User) context.Context { + return context.WithValue(ctx, userKey, user) +} + +func UserFromContext(ctx context.Context) *User { + v := ctx.Value(userKey) + if v == nil { + return nil + } + return v.(*User) +} diff --git a/common/type.go b/common/type.go index 824ebd044..272c0cefd 100644 --- a/common/type.go +++ b/common/type.go @@ -25,7 +25,7 @@ func CreateObject(ctx context.Context, config interface{}) (interface{}, error) configType := reflect.TypeOf(config) creator, found := typeCreatorRegistry[configType] if !found { - return nil, errors.New("Common: " + configType.Name() + " is not registered.") + return nil, errors.New("Common: " + configType.String() + " is not registered.") } return creator(ctx, config) } diff --git a/config.go b/config.go deleted file mode 100644 index 44318792a..000000000 --- a/config.go +++ /dev/null @@ -1,55 +0,0 @@ -package core - -import ( - "v2ray.com/core/common" - "v2ray.com/core/common/net" -) - -func (v *AllocationStrategy) GetConcurrencyValue() uint32 { - if v == nil || v.Concurrency == nil { - return 3 - } - return v.Concurrency.Value -} - -func (v *AllocationStrategy) GetRefreshValue() uint32 { - if v == nil || v.Refresh == nil { - return 5 - } - return v.Refresh.Value -} - -func (v *InboundConnectionConfig) GetAllocationStrategyValue() *AllocationStrategy { - if v.AllocationStrategy == nil { - return &AllocationStrategy{} - } - return v.AllocationStrategy -} - -func (v *InboundConnectionConfig) GetListenOnValue() net.Address { - if v.GetListenOn() == nil { - return net.AnyIP - } - return v.ListenOn.AsAddress() -} - -func (v *InboundConnectionConfig) GetTypedSettings() (interface{}, error) { - if v.GetSettings() == nil { - return nil, common.ErrBadConfiguration - } - return v.GetSettings().GetInstance() -} - -func (v *OutboundConnectionConfig) GetTypedSettings() (interface{}, error) { - if v.GetSettings() == nil { - return nil, common.ErrBadConfiguration - } - return v.GetSettings().GetInstance() -} - -func (v *OutboundConnectionConfig) GetSendThroughValue() net.Address { - if v.GetSendThrough() == nil { - return net.AnyIP - } - return v.SendThrough.AsAddress() -} diff --git a/config.pb.go b/config.pb.go index 658153ece..b88e8ba9f 100644 --- a/config.pb.go +++ b/config.pb.go @@ -3,11 +3,9 @@ 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_common_net "v2ray.com/core/common/net" -import v2ray_core_common_net1 "v2ray.com/core/common/net" import v2ray_core_common_log "v2ray.com/core/common/log" -import v2ray_core_transport_internet "v2ray.com/core/transport/internet" import v2ray_core_transport "v2ray.com/core/transport" // Reference imports to suppress errors if they are not otherwise used. @@ -43,226 +41,12 @@ func (x ConfigFormat) String() string { } func (ConfigFormat) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } -type AllocationStrategy_Type int32 - -const ( - // Always allocate all connection handlers. - AllocationStrategy_Always AllocationStrategy_Type = 0 - // Randomly allocate specific range of handlers. - AllocationStrategy_Random AllocationStrategy_Type = 1 - // External. Not supported yet. - AllocationStrategy_External AllocationStrategy_Type = 2 -) - -var AllocationStrategy_Type_name = map[int32]string{ - 0: "Always", - 1: "Random", - 2: "External", -} -var AllocationStrategy_Type_value = map[string]int32{ - "Always": 0, - "Random": 1, - "External": 2, -} - -func (x AllocationStrategy_Type) String() string { - return proto.EnumName(AllocationStrategy_Type_name, int32(x)) -} -func (AllocationStrategy_Type) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} } - -type AllocationStrategyConcurrency struct { - Value uint32 `protobuf:"varint,1,opt,name=value" json:"value,omitempty"` -} - -func (m *AllocationStrategyConcurrency) Reset() { *m = AllocationStrategyConcurrency{} } -func (m *AllocationStrategyConcurrency) String() string { return proto.CompactTextString(m) } -func (*AllocationStrategyConcurrency) ProtoMessage() {} -func (*AllocationStrategyConcurrency) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -func (m *AllocationStrategyConcurrency) GetValue() uint32 { - if m != nil { - return m.Value - } - return 0 -} - -type AllocationStrategyRefresh struct { - Value uint32 `protobuf:"varint,1,opt,name=value" json:"value,omitempty"` -} - -func (m *AllocationStrategyRefresh) Reset() { *m = AllocationStrategyRefresh{} } -func (m *AllocationStrategyRefresh) String() string { return proto.CompactTextString(m) } -func (*AllocationStrategyRefresh) ProtoMessage() {} -func (*AllocationStrategyRefresh) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *AllocationStrategyRefresh) GetValue() uint32 { - if m != nil { - return m.Value - } - return 0 -} - -type AllocationStrategy struct { - Type AllocationStrategy_Type `protobuf:"varint,1,opt,name=type,enum=v2ray.core.AllocationStrategy_Type" json:"type,omitempty"` - // Number of handlers (ports) running in parallel. - // Default value is 3 if unset. - Concurrency *AllocationStrategyConcurrency `protobuf:"bytes,2,opt,name=concurrency" json:"concurrency,omitempty"` - // Number of minutes before a handler is regenerated. - // Default value is 5 if unset. - Refresh *AllocationStrategyRefresh `protobuf:"bytes,3,opt,name=refresh" json:"refresh,omitempty"` -} - -func (m *AllocationStrategy) Reset() { *m = AllocationStrategy{} } -func (m *AllocationStrategy) String() string { return proto.CompactTextString(m) } -func (*AllocationStrategy) ProtoMessage() {} -func (*AllocationStrategy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } - -func (m *AllocationStrategy) GetType() AllocationStrategy_Type { - if m != nil { - return m.Type - } - return AllocationStrategy_Always -} - -func (m *AllocationStrategy) GetConcurrency() *AllocationStrategyConcurrency { - if m != nil { - return m.Concurrency - } - return nil -} - -func (m *AllocationStrategy) GetRefresh() *AllocationStrategyRefresh { - if m != nil { - return m.Refresh - } - return nil -} - -// Config for an inbound connection handler. -type InboundConnectionConfig struct { - // Protocol specific settings. Must be one of the supported protocols. - Settings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,1,opt,name=settings" json:"settings,omitempty"` - // Range of port number to run on. Both inclusive. - PortRange *v2ray_core_common_net.PortRange `protobuf:"bytes,2,opt,name=port_range,json=portRange" json:"port_range,omitempty"` - // IP address to listen on. 0.0.0.0 if unset. - ListenOn *v2ray_core_common_net1.IPOrDomain `protobuf:"bytes,3,opt,name=listen_on,json=listenOn" json:"listen_on,omitempty"` - // Tag of this handler. - Tag string `protobuf:"bytes,4,opt,name=tag" json:"tag,omitempty"` - AllocationStrategy *AllocationStrategy `protobuf:"bytes,5,opt,name=allocation_strategy,json=allocationStrategy" json:"allocation_strategy,omitempty"` - StreamSettings *v2ray_core_transport_internet.StreamConfig `protobuf:"bytes,6,opt,name=stream_settings,json=streamSettings" json:"stream_settings,omitempty"` - AllowPassiveConnection bool `protobuf:"varint,7,opt,name=allow_passive_connection,json=allowPassiveConnection" json:"allow_passive_connection,omitempty"` -} - -func (m *InboundConnectionConfig) Reset() { *m = InboundConnectionConfig{} } -func (m *InboundConnectionConfig) String() string { return proto.CompactTextString(m) } -func (*InboundConnectionConfig) ProtoMessage() {} -func (*InboundConnectionConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } - -func (m *InboundConnectionConfig) GetSettings() *v2ray_core_common_serial.TypedMessage { - if m != nil { - return m.Settings - } - return nil -} - -func (m *InboundConnectionConfig) GetPortRange() *v2ray_core_common_net.PortRange { - if m != nil { - return m.PortRange - } - return nil -} - -func (m *InboundConnectionConfig) GetListenOn() *v2ray_core_common_net1.IPOrDomain { - if m != nil { - return m.ListenOn - } - return nil -} - -func (m *InboundConnectionConfig) GetTag() string { - if m != nil { - return m.Tag - } - return "" -} - -func (m *InboundConnectionConfig) GetAllocationStrategy() *AllocationStrategy { - if m != nil { - return m.AllocationStrategy - } - return nil -} - -func (m *InboundConnectionConfig) GetStreamSettings() *v2ray_core_transport_internet.StreamConfig { - if m != nil { - return m.StreamSettings - } - return nil -} - -func (m *InboundConnectionConfig) GetAllowPassiveConnection() bool { - if m != nil { - return m.AllowPassiveConnection - } - return false -} - -// Config for an outbound connection handler. -type OutboundConnectionConfig struct { - Settings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,1,opt,name=settings" json:"settings,omitempty"` - // IP address to send data through. 0.0.0.0 if unset. - SendThrough *v2ray_core_common_net1.IPOrDomain `protobuf:"bytes,2,opt,name=send_through,json=sendThrough" json:"send_through,omitempty"` - StreamSettings *v2ray_core_transport_internet.StreamConfig `protobuf:"bytes,3,opt,name=stream_settings,json=streamSettings" json:"stream_settings,omitempty"` - ProxySettings *v2ray_core_transport_internet.ProxyConfig `protobuf:"bytes,5,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"` - Tag string `protobuf:"bytes,4,opt,name=tag" json:"tag,omitempty"` -} - -func (m *OutboundConnectionConfig) Reset() { *m = OutboundConnectionConfig{} } -func (m *OutboundConnectionConfig) String() string { return proto.CompactTextString(m) } -func (*OutboundConnectionConfig) ProtoMessage() {} -func (*OutboundConnectionConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } - -func (m *OutboundConnectionConfig) GetSettings() *v2ray_core_common_serial.TypedMessage { - if m != nil { - return m.Settings - } - return nil -} - -func (m *OutboundConnectionConfig) GetSendThrough() *v2ray_core_common_net1.IPOrDomain { - if m != nil { - return m.SendThrough - } - return nil -} - -func (m *OutboundConnectionConfig) GetStreamSettings() *v2ray_core_transport_internet.StreamConfig { - if m != nil { - return m.StreamSettings - } - return nil -} - -func (m *OutboundConnectionConfig) GetProxySettings() *v2ray_core_transport_internet.ProxyConfig { - if m != nil { - return m.ProxySettings - } - return nil -} - -func (m *OutboundConnectionConfig) GetTag() string { - if m != nil { - return m.Tag - } - return "" -} - type Config struct { // Inbound handler configurations. Must have at least one item. - Inbound []*InboundConnectionConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"` + Inbound []*v2ray_core_app_proxyman.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 []*OutboundConnectionConfig `protobuf:"bytes,2,rep,name=outbound" json:"outbound,omitempty"` - Log *v2ray_core_common_log.Config `protobuf:"bytes,3,opt,name=log" json:"log,omitempty"` + Outbound []*v2ray_core_app_proxyman.OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound" json:"outbound,omitempty"` + Log *v2ray_core_common_log.Config `protobuf:"bytes,3,opt,name=log" json:"log,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 *v2ray_core_transport.Config `protobuf:"bytes,5,opt,name=transport" json:"transport,omitempty"` @@ -271,16 +55,16 @@ 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{5} } +func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } -func (m *Config) GetInbound() []*InboundConnectionConfig { +func (m *Config) GetInbound() []*v2ray_core_app_proxyman.InboundHandlerConfig { if m != nil { return m.Inbound } return nil } -func (m *Config) GetOutbound() []*OutboundConnectionConfig { +func (m *Config) GetOutbound() []*v2ray_core_app_proxyman.OutboundHandlerConfig { if m != nil { return m.Outbound } @@ -309,65 +93,34 @@ func (m *Config) GetTransport() *v2ray_core_transport.Config { } func init() { - proto.RegisterType((*AllocationStrategyConcurrency)(nil), "v2ray.core.AllocationStrategyConcurrency") - proto.RegisterType((*AllocationStrategyRefresh)(nil), "v2ray.core.AllocationStrategyRefresh") - proto.RegisterType((*AllocationStrategy)(nil), "v2ray.core.AllocationStrategy") - proto.RegisterType((*InboundConnectionConfig)(nil), "v2ray.core.InboundConnectionConfig") - proto.RegisterType((*OutboundConnectionConfig)(nil), "v2ray.core.OutboundConnectionConfig") proto.RegisterType((*Config)(nil), "v2ray.core.Config") proto.RegisterEnum("v2ray.core.ConfigFormat", ConfigFormat_name, ConfigFormat_value) - proto.RegisterEnum("v2ray.core.AllocationStrategy_Type", AllocationStrategy_Type_name, AllocationStrategy_Type_value) } func init() { proto.RegisterFile("v2ray.com/core/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 745 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x95, 0xd1, 0x6e, 0xd3, 0x3a, - 0x1c, 0xc6, 0x97, 0xb6, 0xeb, 0xda, 0x7f, 0xb7, 0x9e, 0xca, 0xe7, 0xe8, 0x9c, 0x9c, 0xc1, 0x50, - 0x29, 0xdb, 0x28, 0x03, 0xa5, 0xa3, 0x08, 0x31, 0x21, 0xc1, 0xd8, 0x3a, 0x90, 0x06, 0x82, 0x16, - 0x77, 0xe2, 0x82, 0x9b, 0xc8, 0x4b, 0xbd, 0x2c, 0x52, 0x62, 0x47, 0xb6, 0xbb, 0x2d, 0x8f, 0xc0, - 0xab, 0x70, 0xc5, 0xab, 0xf0, 0x04, 0xbc, 0x0a, 0x72, 0x92, 0xa6, 0x1d, 0x6d, 0xb7, 0x49, 0x88, - 0xbb, 0x34, 0xf9, 0x7e, 0x9f, 0x9d, 0xef, 0xfb, 0xc7, 0x85, 0x5b, 0x67, 0x6d, 0x41, 0x22, 0xcb, - 0xe1, 0x41, 0xcb, 0xe1, 0x82, 0xb6, 0x1c, 0xce, 0x4e, 0x3c, 0xd7, 0x0a, 0x05, 0x57, 0x1c, 0xc1, - 0xe8, 0xa1, 0xa0, 0xab, 0xdb, 0x53, 0xc2, 0x20, 0xe0, 0xac, 0x25, 0xa9, 0xf0, 0x88, 0xdf, 0x52, - 0x51, 0x48, 0x07, 0x76, 0x40, 0xa5, 0x24, 0x2e, 0x4d, 0xe8, 0xd5, 0xf5, 0xd9, 0x04, 0xa3, 0xaa, - 0x15, 0x72, 0xa1, 0x52, 0xd5, 0xfd, 0xf9, 0x2a, 0x32, 0x18, 0x08, 0x2a, 0x65, 0x2a, 0xdc, 0x9c, - 0x2d, 0xf4, 0xb9, 0x7b, 0x69, 0xd3, 0xab, 0xd6, 0x2f, 0x3a, 0x25, 0x08, 0x93, 0x7a, 0xc1, 0x96, - 0xc7, 0x14, 0x15, 0xda, 0xf8, 0x92, 0x7e, 0x63, 0xae, 0x7e, 0x52, 0xd6, 0x78, 0x0a, 0x6b, 0x7b, - 0xbe, 0xcf, 0x1d, 0xa2, 0x3c, 0xce, 0xfa, 0x4a, 0x10, 0x45, 0xdd, 0xa8, 0xc3, 0x99, 0x33, 0x14, - 0x82, 0x32, 0x27, 0x42, 0xff, 0xc0, 0xe2, 0x19, 0xf1, 0x87, 0xd4, 0x34, 0xea, 0x46, 0x73, 0x05, - 0x27, 0x3f, 0x1a, 0x8f, 0xe1, 0xff, 0x69, 0x0c, 0xd3, 0x13, 0x41, 0xe5, 0xe9, 0x1c, 0xe4, 0x4b, - 0x0e, 0xd0, 0x34, 0x83, 0x9e, 0x41, 0x41, 0xa7, 0x1c, 0x6b, 0xab, 0xed, 0x7b, 0xd6, 0xb8, 0x1b, - 0x6b, 0x5a, 0x6d, 0x1d, 0x45, 0x21, 0xc5, 0x31, 0x80, 0xde, 0x41, 0xc5, 0x19, 0xef, 0xd3, 0xcc, - 0xd5, 0x8d, 0x66, 0xa5, 0xfd, 0xe0, 0x6a, 0x7e, 0xe2, 0xc5, 0xf0, 0x24, 0x8d, 0x76, 0x61, 0x49, - 0x24, 0xbb, 0x37, 0xf3, 0xb1, 0xd1, 0xc6, 0xd5, 0x46, 0xe9, 0xab, 0xe2, 0x11, 0xd5, 0x78, 0x04, - 0x05, 0xbd, 0x37, 0x04, 0x50, 0xdc, 0xf3, 0xcf, 0x49, 0x24, 0x6b, 0x0b, 0xfa, 0x1a, 0x13, 0x36, - 0xe0, 0x41, 0xcd, 0x40, 0xcb, 0x50, 0x7a, 0x7d, 0xa1, 0x7b, 0x22, 0x7e, 0x2d, 0xd7, 0xf8, 0x9e, - 0x87, 0xff, 0x0e, 0xd9, 0x31, 0x1f, 0xb2, 0x41, 0x87, 0x33, 0x46, 0x1d, 0xed, 0xdd, 0x89, 0x7b, - 0x41, 0xfb, 0x50, 0x92, 0x54, 0x29, 0x8f, 0xb9, 0x32, 0x0e, 0xa5, 0xd2, 0xde, 0x9c, 0xdc, 0x4b, - 0x32, 0x1f, 0x56, 0x32, 0xa0, 0x71, 0x1e, 0x83, 0xf7, 0xc9, 0x7c, 0xe2, 0x8c, 0x43, 0xbb, 0x00, - 0xba, 0x6a, 0x5b, 0x10, 0xe6, 0xd2, 0x34, 0x9a, 0xfa, 0x0c, 0x17, 0x46, 0x95, 0xd5, 0xe3, 0x42, - 0x61, 0xad, 0xc3, 0xe5, 0x70, 0x74, 0x89, 0x5e, 0x42, 0xd9, 0xf7, 0xa4, 0xa2, 0xcc, 0xe6, 0x2c, - 0x4d, 0xe4, 0xee, 0x1c, 0xfe, 0xb0, 0xd7, 0x15, 0x07, 0x3c, 0x20, 0x1e, 0xc3, 0xa5, 0x84, 0xe9, - 0x32, 0x54, 0x83, 0xbc, 0x22, 0xae, 0x59, 0xa8, 0x1b, 0xcd, 0x32, 0xd6, 0x97, 0xa8, 0x0b, 0x7f, - 0x93, 0x2c, 0x46, 0x5b, 0xa6, 0x39, 0x9a, 0x8b, 0xb1, 0xf7, 0x9d, 0x6b, 0xd2, 0x46, 0x64, 0x7a, - 0x70, 0x8e, 0xe0, 0x2f, 0xa9, 0x04, 0x25, 0x81, 0x9d, 0xc5, 0x55, 0x8c, 0xcd, 0x1e, 0x4e, 0x9a, - 0x65, 0x63, 0x6f, 0x8d, 0x3e, 0x13, 0xab, 0x1f, 0x53, 0x49, 0xda, 0xb8, 0x9a, 0x78, 0xf4, 0x47, - 0xc9, 0xed, 0x80, 0xa9, 0xd7, 0x3a, 0xb7, 0x43, 0x22, 0xa5, 0x77, 0x46, 0x6d, 0x27, 0xeb, 0xc7, - 0x5c, 0xaa, 0x1b, 0xcd, 0x12, 0xfe, 0x37, 0x7e, 0xde, 0x4b, 0x1e, 0x8f, 0xdb, 0x6b, 0xfc, 0xc8, - 0x81, 0xd9, 0x1d, 0xaa, 0x3f, 0x57, 0xea, 0x01, 0x2c, 0x4b, 0xca, 0x06, 0xb6, 0x3a, 0x15, 0x7c, - 0xe8, 0x9e, 0xa6, 0xb5, 0xde, 0xa0, 0x96, 0x8a, 0xc6, 0x8e, 0x12, 0x6a, 0x56, 0x6c, 0xf9, 0xdf, - 0x8f, 0xed, 0x23, 0x54, 0x43, 0xc1, 0x2f, 0xa2, 0xb1, 0x69, 0x52, 0xec, 0xd6, 0x35, 0xa6, 0x3d, - 0x0d, 0xa5, 0x9e, 0x2b, 0xb1, 0x43, 0x66, 0x39, 0x35, 0x42, 0x8d, 0x6f, 0x39, 0x28, 0xa6, 0x79, - 0xbe, 0x80, 0x25, 0x2f, 0xf9, 0x7e, 0x4c, 0xa3, 0x9e, 0x6f, 0x56, 0x2e, 0x1f, 0x1c, 0x73, 0x3e, - 0x2d, 0x3c, 0x62, 0xd0, 0x2b, 0x28, 0xf1, 0xb4, 0x2a, 0x33, 0x17, 0xf3, 0xeb, 0x93, 0xfc, 0xbc, - 0x1a, 0x71, 0x46, 0xa1, 0x16, 0xe4, 0x7d, 0xee, 0xa6, 0xd1, 0xad, 0xcd, 0xe8, 0xc0, 0xe7, 0xae, - 0x95, 0x52, 0x5a, 0x89, 0x76, 0x20, 0x4f, 0xc2, 0xd0, 0x2c, 0xc4, 0xab, 0xdd, 0xb4, 0x7c, 0x8d, - 0xa0, 0xe7, 0x50, 0xce, 0x92, 0x4b, 0x63, 0xbd, 0x3d, 0x3b, 0xd6, 0x74, 0xbd, 0xb1, 0x7c, 0x6b, - 0x13, 0x96, 0x93, 0x9b, 0x6f, 0xb8, 0x08, 0x88, 0xd2, 0xc7, 0x50, 0x4f, 0x9f, 0xfb, 0xc7, 0xc3, - 0x93, 0xda, 0x02, 0x2a, 0x41, 0xe1, 0x6d, 0xbf, 0xfb, 0xa1, 0x66, 0xec, 0x6f, 0x43, 0xd5, 0xe1, - 0xc1, 0x84, 0xeb, 0x7e, 0x25, 0xe1, 0x62, 0xf5, 0xe7, 0x82, 0xbe, 0xf5, 0x35, 0x07, 0x9f, 0xda, - 0x98, 0x44, 0x56, 0x87, 0x0b, 0x7a, 0x5c, 0x8c, 0xff, 0x3f, 0x9e, 0xfc, 0x0c, 0x00, 0x00, 0xff, - 0xff, 0xff, 0x44, 0xf3, 0xc8, 0x6a, 0x07, 0x00, 0x00, + // 338 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x91, 0xdf, 0x4a, 0x02, 0x41, + 0x14, 0xc6, 0xd3, 0x35, 0xb3, 0xa3, 0x84, 0xcc, 0xd5, 0x62, 0x05, 0x12, 0x24, 0x12, 0x34, 0x2b, + 0xdb, 0x4d, 0x74, 0xa9, 0xd0, 0x1f, 0xa1, 0x0c, 0x8b, 0x2e, 0xba, 0x89, 0x71, 0x1d, 0x17, 0x61, + 0x67, 0xce, 0x30, 0xbb, 0x46, 0xfb, 0x4a, 0x3d, 0x47, 0x0f, 0x16, 0x3b, 0xe3, 0xaa, 0x6b, 0x76, + 0x7b, 0xe6, 0xfb, 0xfd, 0x0e, 0xe7, 0x1b, 0x38, 0xfe, 0xf4, 0x35, 0x4b, 0x69, 0x80, 0xc2, 0x0b, + 0x50, 0x73, 0x2f, 0x40, 0x39, 0x9b, 0x87, 0x54, 0x69, 0x4c, 0x90, 0x40, 0xfe, 0xa8, 0x79, 0xab, + 0xbb, 0x15, 0x64, 0x4a, 0x79, 0x4a, 0xe3, 0x57, 0x2a, 0x98, 0x2c, 0x50, 0xad, 0xde, 0x1f, 0xa5, + 0x10, 0x28, 0xbd, 0x98, 0xeb, 0x39, 0x8b, 0xbc, 0x24, 0x55, 0x7c, 0xfa, 0x21, 0x78, 0x1c, 0xb3, + 0x90, 0x2f, 0x89, 0xce, 0x6e, 0x22, 0xc2, 0xb0, 0x68, 0x3e, 0xdf, 0xca, 0x25, 0x9a, 0xc9, 0x58, + 0xa1, 0x4e, 0x0a, 0xb1, 0xb3, 0x9f, 0x32, 0x54, 0x07, 0x66, 0x40, 0xee, 0xe0, 0x60, 0x2e, 0x27, + 0xb8, 0x90, 0x53, 0xb7, 0xd4, 0x76, 0xba, 0x75, 0xff, 0x92, 0xae, 0x6f, 0xa2, 0x4c, 0x29, 0x9a, + 0xdf, 0x40, 0x1f, 0x6c, 0xee, 0x9e, 0xc9, 0x69, 0xc4, 0xb5, 0xe5, 0xc7, 0x39, 0x4d, 0x86, 0x50, + 0xc3, 0x45, 0x62, 0x4d, 0x65, 0x63, 0xa2, 0xff, 0x9a, 0x46, 0xcb, 0x60, 0x51, 0xb5, 0xe2, 0x89, + 0x07, 0x4e, 0x84, 0xa1, 0xeb, 0xb4, 0x4b, 0xdd, 0xba, 0x7f, 0xba, 0xa9, 0xb1, 0x87, 0xd3, 0x08, + 0x43, 0xba, 0xa4, 0xb2, 0x24, 0xb9, 0x06, 0x87, 0x29, 0xe5, 0x56, 0xcc, 0xde, 0xce, 0x0e, 0xc0, + 0x76, 0x4b, 0x5f, 0xb3, 0x6e, 0x1f, 0x6d, 0xb5, 0xe3, 0x0c, 0x21, 0x37, 0x70, 0xb8, 0x2a, 0xc9, + 0xdd, 0x37, 0x0b, 0x4f, 0x36, 0xf9, 0xd5, 0x63, 0xbe, 0x6f, 0x1d, 0xbf, 0xe8, 0x40, 0xc3, 0x0e, + 0x6f, 0x51, 0x0b, 0x96, 0x90, 0x06, 0xd4, 0x9e, 0xb3, 0x7e, 0x27, 0x8b, 0x59, 0x73, 0x8f, 0xd4, + 0xa0, 0x32, 0x7c, 0x19, 0x3d, 0x35, 0x4b, 0xfd, 0x1e, 0x1c, 0x05, 0x28, 0x36, 0xac, 0xfd, 0xba, + 0xe5, 0x4c, 0xfa, 0xbd, 0x92, 0x8d, 0xbe, 0xcb, 0xf0, 0xe6, 0x8f, 0x59, 0x4a, 0x07, 0xa8, 0xf9, + 0xa4, 0x6a, 0xfe, 0xe9, 0xea, 0x37, 0x00, 0x00, 0xff, 0xff, 0xa7, 0x35, 0x52, 0xbd, 0x7d, 0x02, + 0x00, 0x00, } diff --git a/config.proto b/config.proto index db5089fd5..a560371e1 100644 --- a/config.proto +++ b/config.proto @@ -6,11 +6,9 @@ option go_package = "core"; option java_package = "com.v2ray.core"; option java_outer_classname = "ConfigProto"; +import "v2ray.com/core/app/proxyman/config.proto"; import "v2ray.com/core/common/serial/typed_message.proto"; -import "v2ray.com/core/common/net/port.proto"; -import "v2ray.com/core/common/net/address.proto"; import "v2ray.com/core/common/log/config.proto"; -import "v2ray.com/core/transport/internet/config.proto"; import "v2ray.com/core/transport/config.proto"; // Configuration serialization format. @@ -19,78 +17,16 @@ enum ConfigFormat { JSON = 1; } -message AllocationStrategyConcurrency { - uint32 value = 1; -} - -message AllocationStrategyRefresh { - uint32 value = 1; -} - -message AllocationStrategy { - enum Type { - // Always allocate all connection handlers. - Always = 0; - - // Randomly allocate specific range of handlers. - Random = 1; - - // External. Not supported yet. - External = 2; - } - - Type type = 1; - - // Number of handlers (ports) running in parallel. - // Default value is 3 if unset. - AllocationStrategyConcurrency concurrency = 2; - - // Number of minutes before a handler is regenerated. - // Default value is 5 if unset. - AllocationStrategyRefresh refresh = 3; -} - -// Config for an inbound connection handler. -message InboundConnectionConfig { - // Protocol specific settings. Must be one of the supported protocols. - v2ray.core.common.serial.TypedMessage settings = 1; - - // Range of port number to run on. Both inclusive. - v2ray.core.common.net.PortRange port_range = 2; - - // IP address to listen on. 0.0.0.0 if unset. - v2ray.core.common.net.IPOrDomain listen_on = 3; - - // Tag of this handler. - string tag = 4; - - AllocationStrategy allocation_strategy = 5; - - v2ray.core.transport.internet.StreamConfig stream_settings = 6; - - bool allow_passive_connection = 7; -} - -// Config for an outbound connection handler. -message OutboundConnectionConfig { - v2ray.core.common.serial.TypedMessage settings = 1; - - // IP address to send data through. 0.0.0.0 if unset. - v2ray.core.common.net.IPOrDomain send_through = 2; - v2ray.core.transport.internet.StreamConfig stream_settings = 3; - v2ray.core.transport.internet.ProxyConfig proxy_settings = 5; - string tag = 4; -} - message Config { // Inbound handler configurations. Must have at least one item. - repeated InboundConnectionConfig inbound = 1; + repeated v2ray.core.app.proxyman.InboundHandlerConfig inbound = 1; // Outbound handler configurations. Must have at least one item. The first item is used as default for routing. - repeated OutboundConnectionConfig outbound = 2; + repeated v2ray.core.app.proxyman.OutboundHandlerConfig outbound = 2; + v2ray.core.common.log.Config log = 3; // App configuration. Must be one in the app directory. repeated v2ray.core.common.serial.TypedMessage app = 4; v2ray.core.transport.Config transport = 5; -} \ No newline at end of file +} diff --git a/inbound_detour.go b/inbound_detour.go deleted file mode 100644 index 9d5642f41..000000000 --- a/inbound_detour.go +++ /dev/null @@ -1,11 +0,0 @@ -package core - -import ( - "v2ray.com/core/proxy" -) - -type InboundDetourHandler interface { - Start() error - Close() - GetConnectionHandler() (proxy.InboundHandler, int) -} diff --git a/inbound_detour_always.go b/inbound_detour_always.go deleted file mode 100644 index 002428583..000000000 --- a/inbound_detour_always.go +++ /dev/null @@ -1,77 +0,0 @@ -package core - -import ( - "context" - - "v2ray.com/core/app" - "v2ray.com/core/common/dice" - "v2ray.com/core/common/log" - "v2ray.com/core/common/retry" - "v2ray.com/core/proxy" -) - -// InboundDetourHandlerAlways is a handler for inbound detour connections. -type InboundDetourHandlerAlways struct { - space app.Space - config *InboundConnectionConfig - ich []proxy.InboundHandler -} - -func NewInboundDetourHandlerAlways(ctx context.Context, config *InboundConnectionConfig) (*InboundDetourHandlerAlways, error) { - space := app.SpaceFromContext(ctx) - handler := &InboundDetourHandlerAlways{ - space: space, - config: config, - } - ports := config.PortRange - handler.ich = make([]proxy.InboundHandler, 0, ports.To-ports.From+1) - for i := ports.FromPort(); i <= ports.ToPort(); i++ { - ichConfig, err := config.GetTypedSettings() - if err != nil { - return nil, err - } - ich, err := proxy.CreateInboundHandler(proxy.ContextWithInboundMeta(ctx, &proxy.InboundHandlerMeta{ - Address: config.GetListenOnValue(), - Port: i, - Tag: config.Tag, - StreamSettings: config.StreamSettings, - AllowPassiveConnection: config.AllowPassiveConnection, - }), ichConfig) - - if err != nil { - log.Error("Failed to create inbound connection handler: ", err) - return nil, err - } - handler.ich = append(handler.ich, ich) - } - return handler, nil -} - -func (v *InboundDetourHandlerAlways) GetConnectionHandler() (proxy.InboundHandler, int) { - ich := v.ich[dice.Roll(len(v.ich))] - return ich, int(v.config.GetAllocationStrategyValue().GetRefreshValue()) -} - -func (v *InboundDetourHandlerAlways) Close() { - for _, ich := range v.ich { - ich.Close() - } -} - -// Start starts the inbound connection handler. -func (v *InboundDetourHandlerAlways) Start() error { - for _, ich := range v.ich { - err := retry.ExponentialBackoff(10 /* times */, 200 /* ms */).On(func() error { - err := ich.Start() - if err != nil { - log.Error("Failed to start inbound detour:", err) - return err - } - return nil - }) - if err != nil { - return err - } - } - return nil -} diff --git a/inbound_detour_dynamic.go b/inbound_detour_dynamic.go deleted file mode 100644 index f033438da..000000000 --- a/inbound_detour_dynamic.go +++ /dev/null @@ -1,164 +0,0 @@ -package core - -import ( - "context" - "sync" - "time" - - "v2ray.com/core/app" - "v2ray.com/core/common/dice" - "v2ray.com/core/common/log" - "v2ray.com/core/common/net" - "v2ray.com/core/common/retry" - "v2ray.com/core/proxy" -) - -type InboundDetourHandlerDynamic struct { - sync.RWMutex - space app.Space - config *InboundConnectionConfig - portsInUse map[net.Port]bool - ichs []proxy.InboundHandler - ich2Recyle []proxy.InboundHandler - lastRefresh time.Time - ctx context.Context -} - -func NewInboundDetourHandlerDynamic(ctx context.Context, config *InboundConnectionConfig) (*InboundDetourHandlerDynamic, error) { - space := app.SpaceFromContext(ctx) - handler := &InboundDetourHandlerDynamic{ - space: space, - config: config, - portsInUse: make(map[net.Port]bool), - ctx: ctx, - } - handler.ichs = make([]proxy.InboundHandler, config.GetAllocationStrategyValue().GetConcurrencyValue()) - - // To test configuration - ichConfig, err := config.GetTypedSettings() - if err != nil { - return nil, err - } - ich, err := proxy.CreateInboundHandler(proxy.ContextWithInboundMeta(ctx, &proxy.InboundHandlerMeta{ - Address: config.GetListenOnValue(), - Port: 0, - Tag: config.Tag, - StreamSettings: config.StreamSettings, - AllowPassiveConnection: config.AllowPassiveConnection, - }), ichConfig) - if err != nil { - log.Error("Point: Failed to create inbound connection handler: ", err) - return nil, err - } - ich.Close() - - return handler, nil -} - -func (v *InboundDetourHandlerDynamic) pickUnusedPort() net.Port { - delta := int(v.config.PortRange.To) - int(v.config.PortRange.From) + 1 - for { - r := dice.Roll(delta) - port := v.config.PortRange.FromPort() + net.Port(r) - _, used := v.portsInUse[port] - if !used { - return port - } - } -} - -func (v *InboundDetourHandlerDynamic) GetConnectionHandler() (proxy.InboundHandler, int) { - v.RLock() - defer v.RUnlock() - ich := v.ichs[dice.Roll(len(v.ichs))] - until := int(v.config.GetAllocationStrategyValue().GetRefreshValue()) - int((time.Now().Unix()-v.lastRefresh.Unix())/60/1000) - if until < 0 { - until = 0 - } - return ich, int(until) -} - -func (v *InboundDetourHandlerDynamic) Close() { - v.Lock() - defer v.Unlock() - for _, ich := range v.ichs { - ich.Close() - } -} - -func (v *InboundDetourHandlerDynamic) RecyleHandles() { - if v.ich2Recyle != nil { - for _, ich := range v.ich2Recyle { - if ich == nil { - continue - } - port := ich.Port() - ich.Close() - delete(v.portsInUse, port) - } - v.ich2Recyle = nil - } -} - -func (v *InboundDetourHandlerDynamic) refresh() error { - v.lastRefresh = time.Now() - - config := v.config - v.ich2Recyle = v.ichs - newIchs := make([]proxy.InboundHandler, config.GetAllocationStrategyValue().GetConcurrencyValue()) - - for idx := range newIchs { - err := retry.Timed(5, 100).On(func() error { - port := v.pickUnusedPort() - ichConfig, _ := config.GetTypedSettings() - ich, err := proxy.CreateInboundHandler(proxy.ContextWithInboundMeta(v.ctx, &proxy.InboundHandlerMeta{ - Address: config.GetListenOnValue(), - Port: port, Tag: config.Tag, - StreamSettings: config.StreamSettings}), ichConfig) - if err != nil { - delete(v.portsInUse, port) - return err - } - err = ich.Start() - if err != nil { - delete(v.portsInUse, port) - return err - } - v.portsInUse[port] = true - newIchs[idx] = ich - return nil - }) - if err != nil { - log.Error("Point: Failed to create inbound connection handler: ", err) - return err - } - } - - v.Lock() - v.ichs = newIchs - v.Unlock() - - return nil -} - -func (v *InboundDetourHandlerDynamic) Start() error { - err := v.refresh() - if err != nil { - log.Error("Point: Failed to refresh dynamic allocations: ", err) - return err - } - - go func() { - for { - time.Sleep(time.Duration(v.config.GetAllocationStrategyValue().GetRefreshValue())*time.Minute - 1) - v.RecyleHandles() - err := v.refresh() - if err != nil { - log.Error("Point: Failed to refresh dynamic allocations: ", err) - } - time.Sleep(time.Minute) - } - }() - - return nil -} diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 7c6a7686b..8b51f1d13 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -4,7 +4,7 @@ import ( // The following are necessary as they register handlers in their init functions. _ "v2ray.com/core/app/dispatcher/impl" _ "v2ray.com/core/app/dns/server" - _ "v2ray.com/core/app/proxy" + _ "v2ray.com/core/app/proxyman/inbound" _ "v2ray.com/core/app/proxyman/outbound" _ "v2ray.com/core/app/router" diff --git a/proxy/blackhole/blackhole.go b/proxy/blackhole/blackhole.go index 445e96a6f..e88199822 100644 --- a/proxy/blackhole/blackhole.go +++ b/proxy/blackhole/blackhole.go @@ -6,7 +6,6 @@ import ( "time" "v2ray.com/core/common" - "v2ray.com/core/common/net" "v2ray.com/core/transport/ray" ) @@ -27,13 +26,14 @@ func New(ctx context.Context, config *Config) (*Handler, error) { } // Dispatch implements OutboundHandler.Dispatch(). -func (v *Handler) Dispatch(destination net.Destination, ray ray.OutboundRay) { - v.response.WriteTo(ray.OutboundOutput()) +func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay) error { + v.response.WriteTo(outboundRay.OutboundOutput()) // CloseError() will immediately close the connection. // Sleep a little here to make sure the response is sent to client. time.Sleep(time.Millisecond * 500) - ray.OutboundInput().CloseError() - ray.OutboundOutput().CloseError() + outboundRay.OutboundInput().CloseError() + outboundRay.OutboundOutput().CloseError() + return nil } func init() { diff --git a/proxy/context.go b/proxy/context.go index dae22774b..7109a9b55 100644 --- a/proxy/context.go +++ b/proxy/context.go @@ -2,40 +2,24 @@ package proxy import ( "context" + + "v2ray.com/core/common/net" ) type key int const ( - inboundMetaKey key = iota - outboundMetaKey - dialerKey + dialerKey key = iota + sourceKey + destinationKey + originalDestinationKey + inboundDestinationKey + inboundTagKey + outboundTagKey + resolvedIPsKey + allowPassiveConnKey ) -func ContextWithInboundMeta(ctx context.Context, meta *InboundHandlerMeta) context.Context { - return context.WithValue(ctx, inboundMetaKey, meta) -} - -func InboundMetaFromContext(ctx context.Context) *InboundHandlerMeta { - v := ctx.Value(inboundMetaKey) - if v == nil { - return nil - } - return v.(*InboundHandlerMeta) -} - -func ContextWithOutboundMeta(ctx context.Context, meta *OutboundHandlerMeta) context.Context { - return context.WithValue(ctx, outboundMetaKey, meta) -} - -func OutboundMetaFromContext(ctx context.Context) *OutboundHandlerMeta { - v := ctx.Value(outboundMetaKey) - if v == nil { - return nil - } - return v.(*OutboundHandlerMeta) -} - func ContextWithDialer(ctx context.Context, dialer Dialer) context.Context { return context.WithValue(ctx, dialerKey, dialer) } @@ -47,3 +31,93 @@ func DialerFromContext(ctx context.Context) Dialer { } return v.(Dialer) } + +func ContextWithSource(ctx context.Context, src net.Destination) context.Context { + return context.WithValue(ctx, sourceKey, src) +} + +func SourceFromContext(ctx context.Context) net.Destination { + v := ctx.Value(sourceKey) + if v == nil { + return net.Destination{} + } + return v.(net.Destination) +} + +func ContextWithOriginalDestination(ctx context.Context, dest net.Destination) context.Context { + return context.WithValue(ctx, originalDestinationKey, dest) +} + +func OriginalDestinationFromContext(ctx context.Context) net.Destination { + v := ctx.Value(originalDestinationKey) + if v == nil { + return net.Destination{} + } + return v.(net.Destination) +} + +func ContextWithDestination(ctx context.Context, dest net.Destination) context.Context { + return context.WithValue(ctx, destinationKey, dest) +} + +func DestinationFromContext(ctx context.Context) net.Destination { + v := ctx.Value(destinationKey) + if v == nil { + return net.Destination{} + } + return v.(net.Destination) +} + +func ContextWithInboundDestination(ctx context.Context, dest net.Destination) context.Context { + return context.WithValue(ctx, inboundDestinationKey, dest) +} + +func InboundDestinationFromContext(ctx context.Context) net.Destination { + v := ctx.Value(inboundDestinationKey) + if v == nil { + return net.Destination{} + } + return v.(net.Destination) +} + +func ContextWithInboundTag(ctx context.Context, tag string) context.Context { + return context.WithValue(ctx, inboundTagKey, tag) +} + +func InboundTagFromContext(ctx context.Context) string { + v := ctx.Value(inboundTagKey) + if v == nil { + return "" + } + return v.(string) +} + +func ContextWithOutboundTag(ctx context.Context, tag string) context.Context { + return context.WithValue(ctx, outboundTagKey, tag) +} + +func OutboundTagFromContext(ctx context.Context) string { + v := ctx.Value(outboundTagKey) + if v == nil { + return "" + } + return v.(string) +} + +func ContextWithResolveIPs(ctx context.Context, ips []net.Address) context.Context { + return context.WithValue(ctx, resolvedIPsKey, ips) +} + +func ResolvedIPsFromContext(ctx context.Context) ([]net.Address, bool) { + ips, ok := ctx.Value(resolvedIPsKey).([]net.Address) + return ips, ok +} + +func ContextWithAllowPassiveConnection(ctx context.Context, allowPassiveConnection bool) context.Context { + return context.WithValue(ctx, allowPassiveConnKey, allowPassiveConnection) +} + +func AllowPassiveConnectionFromContext(ctx context.Context) (bool, bool) { + allow, ok := ctx.Value(allowPassiveConnKey).(bool) + return allow, ok +} diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 56402963a..79c6b307e 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -2,7 +2,6 @@ package dokodemo import ( "context" - "sync" "v2ray.com/core/app" "v2ray.com/core/app/dispatcher" @@ -14,21 +13,13 @@ import ( "v2ray.com/core/common/signal" "v2ray.com/core/proxy" "v2ray.com/core/transport/internet" - "v2ray.com/core/transport/internet/udp" ) type DokodemoDoor struct { - tcpMutex sync.RWMutex - udpMutex sync.RWMutex config *Config - accepting bool address net.Address port net.Port packetDispatcher dispatcher.Interface - tcpListener *internet.TCPHub - udpHub *udp.Hub - udpServer *udp.Server - meta *proxy.InboundHandlerMeta } func New(ctx context.Context, config *Config) (*DokodemoDoor, error) { @@ -36,10 +27,6 @@ func New(ctx context.Context, config *Config) (*DokodemoDoor, error) { if space == nil { return nil, errors.New("Dokodemo: No space in context.") } - meta := proxy.InboundMetaFromContext(ctx) - if meta == nil { - return nil, errors.New("Dokodemo: No outbound meta in context.") - } if config.NetworkList == nil || config.NetworkList.Size() == 0 { return nil, errors.New("DokodemoDoor: No network specified.") } @@ -47,7 +34,6 @@ func New(ctx context.Context, config *Config) (*DokodemoDoor, error) { config: config, address: config.GetPredefinedAddress(), port: net.Port(config.Port), - meta: meta, } space.OnInitialize(func() error { d.packetDispatcher = dispatcher.FromSpace(space) @@ -59,140 +45,28 @@ func New(ctx context.Context, config *Config) (*DokodemoDoor, error) { return d, nil } -func (v *DokodemoDoor) Port() net.Port { - return v.meta.Port +func (d *DokodemoDoor) Network() net.NetworkList { + return *(d.config.NetworkList) } -func (v *DokodemoDoor) Close() { - v.accepting = false - if v.tcpListener != nil { - v.tcpMutex.Lock() - v.tcpListener.Close() - v.tcpListener = nil - v.tcpMutex.Unlock() - } - if v.udpHub != nil { - v.udpMutex.Lock() - v.udpHub.Close() - v.udpHub = nil - v.udpMutex.Unlock() - } -} - -func (v *DokodemoDoor) Network() net.NetworkList { - return *(v.config.NetworkList) -} - -func (v *DokodemoDoor) Start() error { - if v.accepting { - return nil - } - v.accepting = true - - if v.config.NetworkList.HasNetwork(net.Network_TCP) { - err := v.ListenTCP() - if err != nil { - return err - } - } - if v.config.NetworkList.HasNetwork(net.Network_UDP) { - err := v.ListenUDP() - if err != nil { - return err - } - } - return nil -} - -func (v *DokodemoDoor) ListenUDP() error { - v.udpServer = udp.NewServer(v.packetDispatcher) - udpHub, err := udp.ListenUDP( - v.meta.Address, v.meta.Port, udp.ListenOption{ - Callback: v.handleUDPPackets, - ReceiveOriginalDest: v.config.FollowRedirect, - Concurrency: 2, - }) - if err != nil { - log.Error("Dokodemo failed to listen on ", v.meta.Address, ":", v.meta.Port, ": ", err) - return err - } - v.udpMutex.Lock() - v.udpHub = udpHub - v.udpMutex.Unlock() - return nil -} - -func (v *DokodemoDoor) handleUDPPackets(payload *buf.Buffer, session *proxy.SessionInfo) { - if session.Destination.Network == net.Network_Unknown && v.address != nil && v.port > 0 { - session.Destination = net.UDPDestination(v.address, v.port) - } - if session.Destination.Network == net.Network_Unknown { - log.Info("Dokodemo: Unknown destination, stop forwarding...") - return - } - session.Inbound = v.meta - v.udpServer.Dispatch(session, payload, v.handleUDPResponse) -} - -func (v *DokodemoDoor) handleUDPResponse(dest net.Destination, payload *buf.Buffer) { - defer payload.Release() - v.udpMutex.RLock() - defer v.udpMutex.RUnlock() - if !v.accepting { - return - } - v.udpHub.WriteTo(payload.Bytes(), dest) -} - -func (v *DokodemoDoor) ListenTCP() error { - tcpListener, err := internet.ListenTCP(v.meta.Address, v.meta.Port, v.HandleTCPConnection, v.meta.StreamSettings) - if err != nil { - log.Error("Dokodemo: Failed to listen on ", v.meta.Address, ":", v.meta.Port, ": ", err) - return err - } - v.tcpMutex.Lock() - v.tcpListener = tcpListener - v.tcpMutex.Unlock() - return nil -} - -func (v *DokodemoDoor) HandleTCPConnection(conn internet.Connection) { - defer conn.Close() +func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn internet.Connection) error { + log.Debug("Dokodemo: processing connection from: ", conn.RemoteAddr()) conn.SetReusable(false) - - var dest net.Destination - if v.config.FollowRedirect { - originalDest := GetOriginalDestination(conn) - if originalDest.Network != net.Network_Unknown { - log.Info("Dokodemo: Following redirect to: ", originalDest) - dest = originalDest - } - } - if dest.Network == net.Network_Unknown && v.address != nil && v.port > net.Port(0) { - dest = net.TCPDestination(v.address, v.port) - } - - if dest.Network == net.Network_Unknown { - log.Info("Dokodemo: Unknown destination, stop forwarding...") - return - } - log.Info("Dokodemo: Handling request to ", dest) - - ray := v.packetDispatcher.DispatchToOutbound(&proxy.SessionInfo{ - Source: net.DestinationFromAddr(conn.RemoteAddr()), - Destination: dest, - Inbound: v.meta, + ctx = proxy.ContextWithDestination(ctx, net.Destination{ + Network: network, + Address: d.address, + Port: d.port, }) - - reader := net.NewTimeOutReader(v.config.Timeout, conn) + inboundRay := d.packetDispatcher.DispatchToOutbound(ctx) requestDone := signal.ExecuteAsync(func() error { - defer ray.InboundInput().Close() + defer inboundRay.InboundInput().Close() - v2reader := buf.NewReader(reader) + timedReader := net.NewTimeOutReader(d.config.Timeout, conn) + chunkReader := buf.NewReader(timedReader) - if err := buf.PipeUntilEOF(v2reader, ray.InboundInput()); err != nil { - log.Info("Dokodemo: Failed to transport all TCP request: ", err) + if err := buf.PipeUntilEOF(chunkReader, inboundRay.InboundInput()); err != nil { + log.Info("Dokodemo: Failed to transport request: ", err) return err } @@ -202,18 +76,21 @@ func (v *DokodemoDoor) HandleTCPConnection(conn internet.Connection) { responseDone := signal.ExecuteAsync(func() error { v2writer := buf.NewWriter(conn) - if err := buf.PipeUntilEOF(ray.InboundOutput(), v2writer); err != nil { - log.Info("Dokodemo: Failed to transport all TCP response: ", err) + if err := buf.PipeUntilEOF(inboundRay.InboundOutput(), v2writer); err != nil { + log.Info("Dokodemo: Failed to transport response: ", err) return err } return nil }) if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil { - ray.InboundInput().CloseError() - ray.InboundOutput().CloseError() + inboundRay.InboundInput().CloseError() + inboundRay.InboundOutput().CloseError() log.Info("Dokodemo: Connection ends with ", err) + return err } + + return nil } func init() { diff --git a/proxy/dokodemo/dokodemo_test.go b/proxy/dokodemo/dokodemo_test.go deleted file mode 100644 index 08fe74109..000000000 --- a/proxy/dokodemo/dokodemo_test.go +++ /dev/null @@ -1,173 +0,0 @@ -package dokodemo_test - -import ( - "net" - "testing" - - "context" - - "v2ray.com/core/app" - "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/common/dice" - v2net "v2ray.com/core/common/net" - "v2ray.com/core/proxy" - . "v2ray.com/core/proxy/dokodemo" - "v2ray.com/core/proxy/freedom" - "v2ray.com/core/testing/assert" - "v2ray.com/core/testing/servers/tcp" - "v2ray.com/core/testing/servers/udp" - "v2ray.com/core/transport/internet" - _ "v2ray.com/core/transport/internet/tcp" -) - -func TestDokodemoTCP(t *testing.T) { - assert := assert.On(t) - - tcpServer := &tcp.Server{ - MsgProcessor: func(data []byte) []byte { - buffer := make([]byte, 0, 2048) - buffer = append(buffer, []byte("Processed: ")...) - buffer = append(buffer, data...) - return buffer - }, - } - _, err := tcpServer.Start() - assert.Error(err).IsNil() - - defer tcpServer.Close() - - space := app.NewSpace() - ctx := app.ContextWithSpace(context.Background(), space) - app.AddApplicationToSpace(ctx, new(dispatcher.Config)) - app.AddApplicationToSpace(ctx, new(proxyman.OutboundConfig)) - - ohm := proxyman.OutboundHandlerManagerFromSpace(space) - freedom, err := freedom.New(proxy.ContextWithOutboundMeta(ctx, &proxy.OutboundHandlerMeta{ - Address: v2net.LocalHostIP, - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_TCP, - }, - }), &freedom.Config{}) - assert.Error(err).IsNil() - ohm.SetDefaultHandler(freedom) - - data2Send := "Data to be sent to remote." - - port := v2net.Port(dice.Roll(20000) + 10000) - - ctx = proxy.ContextWithInboundMeta(ctx, &proxy.InboundHandlerMeta{ - Address: v2net.LocalHostIP, - Port: port, - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_TCP, - }}) - - dokodemo, err := New(ctx, &Config{ - Address: v2net.NewIPOrDomain(v2net.LocalHostIP), - Port: uint32(tcpServer.Port), - NetworkList: v2net.Network_TCP.AsList(), - Timeout: 600, - }) - assert.Error(err).IsNil() - defer dokodemo.Close() - - assert.Error(space.Initialize()).IsNil() - - err = dokodemo.Start() - assert.Error(err).IsNil() - assert.Port(port).Equals(dokodemo.Port()) - - tcpClient, err := net.DialTCP("tcp", nil, &net.TCPAddr{ - IP: []byte{127, 0, 0, 1}, - Port: int(port), - Zone: "", - }) - assert.Error(err).IsNil() - - tcpClient.Write([]byte(data2Send)) - tcpClient.CloseWrite() - - response := make([]byte, 1024) - nBytes, err := tcpClient.Read(response) - assert.Error(err).IsNil() - tcpClient.Close() - - assert.String("Processed: " + data2Send).Equals(string(response[:nBytes])) -} - -func TestDokodemoUDP(t *testing.T) { - assert := assert.On(t) - - udpServer := &udp.Server{ - MsgProcessor: func(data []byte) []byte { - buffer := make([]byte, 0, 2048) - buffer = append(buffer, []byte("Processed: ")...) - buffer = append(buffer, data...) - return buffer - }, - } - _, err := udpServer.Start() - assert.Error(err).IsNil() - - defer udpServer.Close() - - space := app.NewSpace() - ctx := app.ContextWithSpace(context.Background(), space) - app.AddApplicationToSpace(ctx, new(dispatcher.Config)) - app.AddApplicationToSpace(ctx, new(proxyman.OutboundConfig)) - - ohm := proxyman.OutboundHandlerManagerFromSpace(space) - freedom, err := freedom.New(proxy.ContextWithOutboundMeta(ctx, &proxy.OutboundHandlerMeta{ - Address: v2net.AnyIP, - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_TCP, - }, - }), &freedom.Config{}) - assert.Error(err).IsNil() - ohm.SetDefaultHandler(freedom) - - data2Send := "Data to be sent to remote." - - port := v2net.Port(dice.Roll(20000) + 10000) - - ctx = proxy.ContextWithInboundMeta(ctx, &proxy.InboundHandlerMeta{ - Address: v2net.LocalHostIP, - Port: port, - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_TCP, - }}) - - dokodemo, err := New(ctx, &Config{ - Address: v2net.NewIPOrDomain(v2net.LocalHostIP), - Port: uint32(udpServer.Port), - NetworkList: v2net.Network_UDP.AsList(), - Timeout: 600, - }) - assert.Error(err).IsNil() - defer dokodemo.Close() - - assert.Error(space.Initialize()).IsNil() - - err = dokodemo.Start() - assert.Error(err).IsNil() - assert.Port(port).Equals(dokodemo.Port()) - - udpClient, err := net.DialUDP("udp", nil, &net.UDPAddr{ - IP: []byte{127, 0, 0, 1}, - Port: int(port), - Zone: "", - }) - assert.Error(err).IsNil() - defer udpClient.Close() - - udpClient.Write([]byte(data2Send)) - - response := make([]byte, 1024) - nBytes, addr, err := udpClient.ReadFromUDP(response) - assert.Error(err).IsNil() - assert.IP(addr.IP).Equals(v2net.LocalHostIP.IP()) - assert.Bytes(response[:nBytes]).Equals([]byte("Processed: " + data2Send)) -} diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index cfbcd2b37..209e99e3f 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -23,7 +23,6 @@ type Handler struct { domainStrategy Config_DomainStrategy timeout uint32 dns dns.Server - meta *proxy.OutboundHandlerMeta } func New(ctx context.Context, config *Config) (*Handler, error) { @@ -31,14 +30,9 @@ func New(ctx context.Context, config *Config) (*Handler, error) { if space == nil { return nil, errors.New("Freedom: No space in context.") } - meta := proxy.OutboundMetaFromContext(ctx) - if meta == nil { - return nil, errors.New("Freedom: No outbound meta in context.") - } f := &Handler{ domainStrategy: config.DomainStrategy, timeout: config.Timeout, - meta: meta, } space.OnInitialize(func() error { if config.DomainStrategy == Config_USE_IP { @@ -75,18 +69,21 @@ func (v *Handler) ResolveIP(destination net.Destination) net.Destination { return newDest } -func (v *Handler) Dispatch(destination net.Destination, ray ray.OutboundRay) { +func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay) error { + destination := proxy.DestinationFromContext(ctx) log.Info("Freedom: Opening connection to ", destination) - input := ray.OutboundInput() - output := ray.OutboundOutput() + input := outboundRay.OutboundInput() + output := outboundRay.OutboundOutput() var conn internet.Connection if v.domainStrategy == Config_USE_IP && destination.Address.Family().IsDomain() { destination = v.ResolveIP(destination) } + + dialer := proxy.DialerFromContext(ctx) err := retry.ExponentialBackoff(5, 100).On(func() error { - rawConn, err := internet.Dial(v.meta.Address, destination, v.meta.GetDialerOptions()) + rawConn, err := dialer.Dial(ctx, destination) if err != nil { return err } @@ -95,7 +92,7 @@ func (v *Handler) Dispatch(destination net.Destination, ray ray.OutboundRay) { }) if err != nil { log.Warning("Freedom: Failed to open connection to ", destination, ": ", err) - return + return err } defer conn.Close() @@ -133,7 +130,10 @@ func (v *Handler) Dispatch(destination net.Destination, ray ray.OutboundRay) { log.Info("Freedom: Connection ending with ", err) input.CloseError() output.CloseError() + return err } + + return nil } func init() { diff --git a/proxy/freedom/freedom_test.go b/proxy/freedom/freedom_test.go deleted file mode 100644 index 5c349b23f..000000000 --- a/proxy/freedom/freedom_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package freedom_test - -import ( - "testing" - - "context" - - "v2ray.com/core/app" - "v2ray.com/core/app/dispatcher" - _ "v2ray.com/core/app/dispatcher/impl" - "v2ray.com/core/app/dns" - _ "v2ray.com/core/app/dns/server" - "v2ray.com/core/app/proxyman" - _ "v2ray.com/core/app/proxyman/outbound" - "v2ray.com/core/app/router" - "v2ray.com/core/common/buf" - v2net "v2ray.com/core/common/net" - "v2ray.com/core/proxy" - . "v2ray.com/core/proxy/freedom" - "v2ray.com/core/testing/assert" - "v2ray.com/core/testing/servers/tcp" - "v2ray.com/core/transport/internet" - _ "v2ray.com/core/transport/internet/tcp" - "v2ray.com/core/transport/ray" -) - -func TestSinglePacket(t *testing.T) { - assert := assert.On(t) - - tcpServer := &tcp.Server{ - MsgProcessor: func(data []byte) []byte { - buffer := make([]byte, 0, 2048) - buffer = append(buffer, []byte("Processed: ")...) - buffer = append(buffer, data...) - return buffer - }, - } - tcpServerAddr, err := tcpServer.Start() - assert.Error(err).IsNil() - - space := app.NewSpace() - ctx := app.ContextWithSpace(context.Background(), space) - ctx = proxy.ContextWithOutboundMeta(ctx, &proxy.OutboundHandlerMeta{ - Address: v2net.AnyIP, - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_TCP, - }, - }) - freedom, err := New(ctx, &Config{}) - assert.Error(err).IsNil() - assert.Error(space.Initialize()).IsNil() - - traffic := ray.NewRay() - data2Send := "Data to be sent to remote" - payload := buf.NewLocal(2048) - payload.Append([]byte(data2Send)) - traffic.InboundInput().Write(payload) - - go freedom.Dispatch(tcpServerAddr, traffic) - traffic.InboundInput().Close() - - respPayload, err := traffic.InboundOutput().Read() - assert.Error(err).IsNil() - assert.String(respPayload.String()).Equals("Processed: Data to be sent to remote") - - tcpServer.Close() -} - -func TestIPResolution(t *testing.T) { - assert := assert.On(t) - - space := app.NewSpace() - ctx := app.ContextWithSpace(context.Background(), space) - assert.Error(app.AddApplicationToSpace(ctx, new(proxyman.OutboundConfig))).IsNil() - assert.Error(app.AddApplicationToSpace(ctx, new(dispatcher.Config))).IsNil() - assert.Error(app.AddApplicationToSpace(ctx, new(router.Config))).IsNil() - assert.Error(app.AddApplicationToSpace(ctx, &dns.Config{ - Hosts: map[string]*v2net.IPOrDomain{ - "v2ray.com": v2net.NewIPOrDomain(v2net.LocalHostIP), - }, - })).IsNil() - - ctx = proxy.ContextWithOutboundMeta(ctx, &proxy.OutboundHandlerMeta{ - Address: v2net.AnyIP, - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_TCP, - }, - }) - freedom, err := New(ctx, &Config{DomainStrategy: Config_USE_IP}) - assert.Error(err).IsNil() - - assert.Error(space.Initialize()).IsNil() - - ipDest := freedom.ResolveIP(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), v2net.Port(80))) - assert.Destination(ipDest).IsTCP() - assert.Address(ipDest.Address).Equals(v2net.LocalHostIP) -} diff --git a/proxy/http/server.go b/proxy/http/server.go index e186871ce..cddd7ec2f 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -25,11 +25,8 @@ import ( // Server is a HTTP proxy server. type Server struct { sync.Mutex - accepting bool packetDispatcher dispatcher.Interface config *ServerConfig - tcpListener *internet.TCPHub - meta *proxy.InboundHandlerMeta } // NewServer creates a new HTTP inbound handler. @@ -38,13 +35,8 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { if space == nil { return nil, errors.New("HTTP|Server: No space in context.") } - meta := proxy.InboundMetaFromContext(ctx) - if meta == nil { - return nil, errors.New("HTTP|Server: No inbound meta from context.") - } s := &Server{ config: config, - meta: meta, } space.OnInitialize(func() error { s.packetDispatcher = dispatcher.FromSpace(space) @@ -56,46 +48,12 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { return s, nil } -// Port implements InboundHandler.Port(). -func (v *Server) Port() v2net.Port { - return v.meta.Port -} - -func (v *Server) Network() v2net.NetworkList { +func (*Server) Network() v2net.NetworkList { return v2net.NetworkList{ Network: []v2net.Network{v2net.Network_TCP}, } } -// Close implements InboundHandler.Close(). -func (v *Server) Close() { - v.accepting = false - if v.tcpListener != nil { - v.Lock() - v.tcpListener.Close() - v.tcpListener = nil - v.Unlock() - } -} - -// Start implements InboundHandler.Start(). -func (v *Server) Start() error { - if v.accepting { - return nil - } - - tcpListener, err := internet.ListenTCP(v.meta.Address, v.meta.Port, v.handleConnection, v.meta.StreamSettings) - if err != nil { - log.Error("HTTP: Failed listen on ", v.meta.Address, ":", v.meta.Port, ": ", err) - return err - } - v.Lock() - v.tcpListener = tcpListener - v.Unlock() - v.accepting = true - return nil -} - func parseHost(rawHost string, defaultPort v2net.Port) (v2net.Destination, error) { port := defaultPort host, rawPort, err := net.SplitHostPort(rawHost) @@ -119,11 +77,10 @@ func parseHost(rawHost string, defaultPort v2net.Port) (v2net.Destination, error return v2net.TCPDestination(v2net.DomainAddress(host), port), nil } -func (v *Server) handleConnection(conn internet.Connection) { - defer conn.Close() +func (s *Server) Process(ctx context.Context, network v2net.Network, conn internet.Connection) error { conn.SetReusable(false) - timedReader := v2net.NewTimeOutReader(v.config.Timeout, conn) + timedReader := v2net.NewTimeOutReader(s.config.Timeout, conn) reader := bufio.OriginalReaderSize(timedReader, 2048) request, err := http.ReadRequest(reader) @@ -131,7 +88,7 @@ func (v *Server) handleConnection(conn internet.Connection) { if errors.Cause(err) != io.EOF { log.Warning("HTTP: Failed to read http request: ", err) } - return + return err } log.Info("HTTP: Request to Method [", request.Method, "] Host [", request.Host, "] with URL [", request.URL, "]") defaultPort := v2net.Port(80) @@ -145,22 +102,18 @@ func (v *Server) handleConnection(conn internet.Connection) { dest, err := parseHost(host, defaultPort) if err != nil { log.Warning("HTTP: Malformed proxy host (", host, "): ", err) - return + return err } log.Access(conn.RemoteAddr(), request.URL, log.AccessAccepted, "") - session := &proxy.SessionInfo{ - Source: v2net.DestinationFromAddr(conn.RemoteAddr()), - Destination: dest, - Inbound: v.meta, - } + ctx = proxy.ContextWithDestination(ctx, dest) if strings.ToUpper(request.Method) == "CONNECT" { - v.handleConnect(request, session, reader, conn) + return s.handleConnect(ctx, request, reader, conn) } else { - v.handlePlainHTTP(request, session, reader, conn) + return s.handlePlainHTTP(ctx, request, reader, conn) } } -func (v *Server) handleConnect(request *http.Request, session *proxy.SessionInfo, reader io.Reader, writer io.Writer) { +func (s *Server) handleConnect(ctx context.Context, request *http.Request, reader io.Reader, writer io.Writer) error { response := &http.Response{ Status: "200 OK", StatusCode: 200, @@ -174,10 +127,10 @@ func (v *Server) handleConnect(request *http.Request, session *proxy.SessionInfo } if err := response.Write(writer); err != nil { log.Warning("HTTP|Server: failed to write back OK response: ", err) - return + return err } - ray := v.packetDispatcher.DispatchToOutbound(session) + ray := s.packetDispatcher.DispatchToOutbound(ctx) requestDone := signal.ExecuteAsync(func() error { defer ray.InboundInput().Close() @@ -201,7 +154,10 @@ func (v *Server) handleConnect(request *http.Request, session *proxy.SessionInfo log.Info("HTTP|Server: Connection ends with: ", err) ray.InboundInput().CloseError() ray.InboundOutput().CloseError() + return err } + + return nil } // @VisibleForTesting @@ -229,7 +185,7 @@ func StripHopByHopHeaders(request *http.Request) { } } -func (v *Server) GenerateResponse(statusCode int, status string) *http.Response { +func generateResponse(statusCode int, status string) *http.Response { hdr := http.Header(make(map[string][]string)) hdr.Set("Connection", "close") return &http.Response{ @@ -245,18 +201,16 @@ func (v *Server) GenerateResponse(statusCode int, status string) *http.Response } } -func (v *Server) handlePlainHTTP(request *http.Request, session *proxy.SessionInfo, reader io.Reader, writer io.Writer) { +func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, reader io.Reader, writer io.Writer) error { if len(request.URL.Host) <= 0 { - response := v.GenerateResponse(400, "Bad Request") - response.Write(writer) - - return + response := generateResponse(400, "Bad Request") + return response.Write(writer) } request.Host = request.URL.Host StripHopByHopHeaders(request) - ray := v.packetDispatcher.DispatchToOutbound(session) + ray := s.packetDispatcher.DispatchToOutbound(ctx) input := ray.InboundInput() output := ray.InboundOutput() @@ -279,7 +233,7 @@ func (v *Server) handlePlainHTTP(request *http.Request, session *proxy.SessionIn response, err := http.ReadResponse(responseReader, request) if err != nil { log.Warning("HTTP: Failed to read response: ", err) - response = v.GenerateResponse(503, "Service Unavailable") + response = generateResponse(503, "Service Unavailable") } responseWriter := bufio.NewWriter(writer) if err := response.Write(responseWriter); err != nil { @@ -296,7 +250,10 @@ func (v *Server) handlePlainHTTP(request *http.Request, session *proxy.SessionIn log.Info("HTTP|Server: Connecton ending with ", err) input.CloseError() output.CloseError() + return err } + + return nil } func init() { diff --git a/proxy/proxy.go b/proxy/proxy.go index ab167a882..71c226760 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -5,63 +5,20 @@ import ( "context" "v2ray.com/core/common/net" - "v2ray.com/core/common/protocol" "v2ray.com/core/transport/internet" "v2ray.com/core/transport/ray" ) -type HandlerState int - -const ( - HandlerStateStopped = HandlerState(0) - HandlerStateRunning = HandlerState(1) -) - -type SessionInfo struct { - Source net.Destination - Destination net.Destination - User *protocol.User - Inbound *InboundHandlerMeta -} - -type InboundHandlerMeta struct { - Tag string - Address net.Address - Port net.Port - AllowPassiveConnection bool - StreamSettings *internet.StreamConfig -} - -type OutboundHandlerMeta struct { - Tag string - Address net.Address - StreamSettings *internet.StreamConfig - ProxySettings *internet.ProxyConfig -} - -func (v *OutboundHandlerMeta) GetDialerOptions() internet.DialerOptions { - return internet.DialerOptions{ - Stream: v.StreamSettings, - Proxy: v.ProxySettings, - } -} - // An InboundHandler handles inbound network connections to V2Ray. type InboundHandler interface { - // Listen starts a InboundHandler. - Start() error - // Close stops the handler to accepting anymore inbound connections. - Close() - // Port returns the port that the handler is listening on. - Port() net.Port - Network() net.NetworkList + + Process(context.Context, net.Network, internet.Connection) error } // An OutboundHandler handles outbound network connection for V2Ray. type OutboundHandler interface { - // Dispatch sends one or more Packets to its destination. - Dispatch(destination net.Destination, ray ray.OutboundRay) + Process(context.Context, ray.OutboundRay) error } // Dialer is used by OutboundHandler for creating outbound connections. diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index 36002a3ae..55cabf18f 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -2,7 +2,6 @@ package shadowsocks import ( "context" - "errors" "v2ray.com/core/common" "v2ray.com/core/common/buf" @@ -20,39 +19,35 @@ import ( // Client is a inbound handler for Shadowsocks protocol type Client struct { serverPicker protocol.ServerPicker - meta *proxy.OutboundHandlerMeta } // NewClient create a new Shadowsocks client. func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { - meta := proxy.OutboundMetaFromContext(ctx) - if meta == nil { - return nil, errors.New("Shadowsocks|Client: No outbound meta in context.") - } serverList := protocol.NewServerList() for _, rec := range config.Server { serverList.AddServer(protocol.NewServerSpecFromPB(*rec)) } client := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), - meta: meta, } return client, nil } -// Dispatch implements OutboundHandler.Dispatch(). -func (v *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { +// Process implements OutboundHandler.Process(). +func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay) error { + destination := proxy.DestinationFromContext(ctx) network := destination.Network var server *protocol.ServerSpec var conn internet.Connection + dialer := proxy.DialerFromContext(ctx) err := retry.ExponentialBackoff(5, 100).On(func() error { server = v.serverPicker.PickServer() dest := server.Destination() dest.Network = network - rawConn, err := internet.Dial(v.meta.Address, dest, v.meta.GetDialerOptions()) + rawConn, err := dialer.Dial(ctx, dest) if err != nil { return err } @@ -62,7 +57,7 @@ func (v *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { }) if err != nil { log.Warning("Shadowsocks|Client: Failed to find an available destination:", err) - return + return err } log.Info("Shadowsocks|Client: Tunneling request to ", destination, " via ", server.Destination()) @@ -83,7 +78,7 @@ func (v *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { rawAccount, err := user.GetTypedAccount() if err != nil { log.Warning("Shadowsocks|Client: Failed to get a valid user account: ", err) - return + return err } account := rawAccount.(*ShadowsocksAccount) request.User = user @@ -97,27 +92,27 @@ func (v *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { bodyWriter, err := WriteTCPRequest(request, bufferedWriter) if err != nil { log.Info("Shadowsocks|Client: Failed to write request: ", err) - return + return err } bufferedWriter.SetBuffered(false) requestDone := signal.ExecuteAsync(func() error { - if err := buf.PipeUntilEOF(ray.OutboundInput(), bodyWriter); err != nil { + if err := buf.PipeUntilEOF(outboundRay.OutboundInput(), bodyWriter); err != nil { return err } return nil }) responseDone := signal.ExecuteAsync(func() error { - defer ray.OutboundOutput().Close() + defer outboundRay.OutboundOutput().Close() responseReader, err := ReadTCPResponse(user, conn) if err != nil { return err } - if err := buf.PipeUntilEOF(responseReader, ray.OutboundOutput()); err != nil { + if err := buf.PipeUntilEOF(responseReader, outboundRay.OutboundOutput()); err != nil { return err } @@ -126,9 +121,12 @@ func (v *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil { log.Info("Shadowsocks|Client: Connection ends with ", err) - ray.OutboundInput().CloseError() - ray.OutboundOutput().CloseError() + outboundRay.OutboundInput().CloseError() + outboundRay.OutboundOutput().CloseError() + return err } + + return nil } if request.Command == protocol.RequestCommandUDP { @@ -139,7 +137,7 @@ func (v *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { } requestDone := signal.ExecuteAsync(func() error { - if err := buf.PipeUntilEOF(ray.OutboundInput(), writer); err != nil { + if err := buf.PipeUntilEOF(outboundRay.OutboundInput(), writer); err != nil { log.Info("Shadowsocks|Client: Failed to transport all UDP request: ", err) return err } @@ -149,14 +147,14 @@ func (v *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { timedReader := net.NewTimeOutReader(16, conn) responseDone := signal.ExecuteAsync(func() error { - defer ray.OutboundOutput().Close() + defer outboundRay.OutboundOutput().Close() reader := &UDPReader{ Reader: timedReader, User: user, } - if err := buf.PipeUntilEOF(reader, ray.OutboundOutput()); err != nil { + if err := buf.PipeUntilEOF(reader, outboundRay.OutboundOutput()); err != nil { log.Info("Shadowsocks|Client: Failed to transport all UDP response: ", err) return err } @@ -165,10 +163,15 @@ func (v *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { if err := signal.ErrorOrFinish2(requestDone, responseDone); err != nil { log.Info("Shadowsocks|Client: Connection ends with ", err) - ray.OutboundInput().CloseError() - ray.OutboundOutput().CloseError() + outboundRay.OutboundInput().CloseError() + outboundRay.OutboundOutput().CloseError() + return err } + + return nil } + + return nil } func init() { diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 72850cbb5..599e0f190 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -23,10 +23,6 @@ type Server struct { config *ServerConfig user *protocol.User account *ShadowsocksAccount - meta *proxy.InboundHandlerMeta - accepting bool - tcpHub *internet.TCPHub - udpHub *udp.Hub udpServer *udp.Server } @@ -35,10 +31,6 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { if space == nil { return nil, errors.New("Shadowsocks|Server: No space in context.") } - meta := proxy.InboundMetaFromContext(ctx) - if meta == nil { - return nil, errors.New("Shadowsocks|Server: No inbound meta in context.") - } if config.GetUser() == nil { return nil, protocol.ErrUserMissing } @@ -51,7 +43,6 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { s := &Server{ config: config, - meta: meta, user: config.GetUser(), account: account, } @@ -67,129 +58,103 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { return s, nil } -func (v *Server) Network() net.NetworkList { +func (s *Server) Network() net.NetworkList { list := net.NetworkList{ Network: []net.Network{net.Network_TCP}, } - if v.config.UdpEnabled { + if s.config.UdpEnabled { list.Network = append(list.Network, net.Network_UDP) } return list } -func (v *Server) Port() net.Port { - return v.meta.Port -} - -func (v *Server) Close() { - v.accepting = false - // TODO: synchronization - if v.tcpHub != nil { - v.tcpHub.Close() - v.tcpHub = nil - } - - if v.udpHub != nil { - v.udpHub.Close() - v.udpHub = nil +func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection) error { + switch network { + case net.Network_TCP: + return s.handleConnection(ctx, conn) + case net.Network_UDP: + return s.handlerUDPPayload(ctx, conn) + default: + return errors.New("Shadowsocks|Server: Unknown network: ", network) } } -func (v *Server) Start() error { - if v.accepting { - return nil - } +func (v *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection) error { + source := proxy.SourceFromContext(ctx) - tcpHub, err := internet.ListenTCP(v.meta.Address, v.meta.Port, v.handleConnection, v.meta.StreamSettings) - if err != nil { - log.Error("Shadowsocks: Failed to listen TCP on ", v.meta.Address, ":", v.meta.Port, ": ", err) - return err - } - v.tcpHub = tcpHub - - if v.config.UdpEnabled { - v.udpServer = udp.NewServer(v.packetDispatcher) - udpHub, err := udp.ListenUDP(v.meta.Address, v.meta.Port, udp.ListenOption{Callback: v.handlerUDPPayload}) + reader := buf.NewReader(conn) + for { + payload, err := reader.Read() if err != nil { - log.Error("Shadowsocks: Failed to listen UDP on ", v.meta.Address, ":", v.meta.Port, ": ", err) - return err + break } - v.udpHub = udpHub - } - v.accepting = true + request, data, err := DecodeUDPPacket(v.user, payload) + if err != nil { + log.Info("Shadowsocks|Server: Skipping invalid UDP packet from: ", source, ": ", err) + log.Access(source, "", log.AccessRejected, err) + payload.Release() + continue + } + + if request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Disabled { + log.Info("Shadowsocks|Server: Client payload enables OTA but server doesn't allow it.") + payload.Release() + continue + } + + if !request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Enabled { + log.Info("Shadowsocks|Server: Client payload disables OTA but server forces it.") + payload.Release() + continue + } + + dest := request.Destination() + log.Access(source, dest, log.AccessAccepted, "") + log.Info("Shadowsocks|Server: Tunnelling request to ", dest) + + ctx = protocol.ContextWithUser(ctx, request.User) + v.udpServer.Dispatch(ctx, dest, data, func(payload *buf.Buffer) { + defer payload.Release() + + data, err := EncodeUDPPacket(request, payload) + if err != nil { + log.Warning("Shadowsocks|Server: Failed to encode UDP packet: ", err) + return + } + defer data.Release() + + conn.Write(data.Bytes()) + }) + } return nil } -func (v *Server) handlerUDPPayload(payload *buf.Buffer, session *proxy.SessionInfo) { - source := session.Source - request, data, err := DecodeUDPPacket(v.user, payload) - if err != nil { - log.Info("Shadowsocks|Server: Skipping invalid UDP packet from: ", source, ": ", err) - log.Access(source, "", log.AccessRejected, err) - payload.Release() - return - } - - if request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Disabled { - log.Info("Shadowsocks|Server: Client payload enables OTA but server doesn't allow it.") - payload.Release() - return - } - - if !request.Option.Has(RequestOptionOneTimeAuth) && v.account.OneTimeAuth == Account_Enabled { - log.Info("Shadowsocks|Server: Client payload disables OTA but server forces it.") - payload.Release() - return - } - - dest := request.Destination() - log.Access(source, dest, log.AccessAccepted, "") - log.Info("Shadowsocks|Server: Tunnelling request to ", dest) - - v.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: dest, User: request.User, Inbound: v.meta}, data, func(destination net.Destination, payload *buf.Buffer) { - defer payload.Release() - - data, err := EncodeUDPPacket(request, payload) - if err != nil { - log.Warning("Shadowsocks|Server: Failed to encode UDP packet: ", err) - return - } - defer data.Release() - - v.udpHub.WriteTo(data.Bytes(), source) - }) -} - -func (v *Server) handleConnection(conn internet.Connection) { - defer conn.Close() +func (s *Server) handleConnection(ctx context.Context, conn internet.Connection) error { conn.SetReusable(false) timedReader := net.NewTimeOutReader(16, conn) bufferedReader := bufio.NewReader(timedReader) - request, bodyReader, err := ReadTCPSession(v.user, bufferedReader) + request, bodyReader, err := ReadTCPSession(s.user, bufferedReader) if err != nil { log.Access(conn.RemoteAddr(), "", log.AccessRejected, err) log.Info("Shadowsocks|Server: Failed to create request from: ", conn.RemoteAddr(), ": ", err) - return + return err } bufferedReader.SetBuffered(false) - userSettings := v.user.GetSettings() + userSettings := s.user.GetSettings() timedReader.SetTimeOut(userSettings.PayloadReadTimeout) dest := request.Destination() log.Access(conn.RemoteAddr(), dest, log.AccessAccepted, "") log.Info("Shadowsocks|Server: Tunnelling request to ", dest) - ray := v.packetDispatcher.DispatchToOutbound(&proxy.SessionInfo{ - Source: net.DestinationFromAddr(conn.RemoteAddr()), - Destination: dest, - User: request.User, - Inbound: v.meta, - }) + ctx = proxy.ContextWithDestination(ctx, dest) + ctx = protocol.ContextWithUser(ctx, request.User) + ray := s.packetDispatcher.DispatchToOutbound(ctx) requestDone := signal.ExecuteAsync(func() error { bufferedWriter := bufio.NewWriter(conn) @@ -234,7 +199,10 @@ func (v *Server) handleConnection(conn internet.Connection) { log.Info("Shadowsocks|Server: Connection ends with ", err) ray.InboundInput().CloseError() ray.InboundOutput().CloseError() + return err } + + return nil } func init() { diff --git a/proxy/socks/client.go b/proxy/socks/client.go index 339698dca..30e4f2fcd 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -2,7 +2,6 @@ package socks import ( "context" - "errors" "v2ray.com/core/common" "v2ray.com/core/common/buf" @@ -18,34 +17,31 @@ import ( type Client struct { serverPicker protocol.ServerPicker - meta *proxy.OutboundHandlerMeta } func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { - meta := proxy.OutboundMetaFromContext(ctx) - if meta == nil { - return nil, errors.New("Socks|Client: No outbound meta in context.") - } serverList := protocol.NewServerList() for _, rec := range config.Server { serverList.AddServer(protocol.NewServerSpecFromPB(*rec)) } client := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), - meta: meta, } return client, nil } -func (c *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { +func (c *Client) Process(ctx context.Context, ray ray.OutboundRay) error { + destination := proxy.DestinationFromContext(ctx) + var server *protocol.ServerSpec var conn internet.Connection + dialer := proxy.DialerFromContext(ctx) err := retry.ExponentialBackoff(5, 100).On(func() error { server = c.serverPicker.PickServer() dest := server.Destination() - rawConn, err := internet.Dial(c.meta.Address, dest, c.meta.GetDialerOptions()) + rawConn, err := dialer.Dial(ctx, dest) if err != nil { return err } @@ -56,7 +52,7 @@ func (c *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { if err != nil { log.Warning("Socks|Client: Failed to find an available destination.") - return + return err } defer conn.Close() @@ -80,7 +76,7 @@ func (c *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { udpRequest, err := ClientHandshake(request, conn, conn) if err != nil { log.Warning("Socks|Client: Failed to establish connection to server: ", err) - return + return err } var requestFunc func() error @@ -94,10 +90,10 @@ func (c *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { return buf.PipeUntilEOF(buf.NewReader(conn), ray.OutboundOutput()) } } else if request.Command == protocol.RequestCommandUDP { - udpConn, err := internet.Dial(c.meta.Address, udpRequest.Destination(), c.meta.GetDialerOptions()) + udpConn, err := dialer.Dial(ctx, udpRequest.Destination()) if err != nil { log.Info("Socks|Client: Failed to create UDP connection: ", err) - return + return err } defer udpConn.Close() requestFunc = func() error { @@ -116,7 +112,10 @@ func (c *Client) Dispatch(destination net.Destination, ray ray.OutboundRay) { log.Info("Socks|Client: Connection ends with ", err) ray.OutboundInput().CloseError() ray.OutboundOutput().CloseError() + return err } + + return nil } func init() { diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index 298741d55..09491f930 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -8,7 +8,6 @@ import ( v2net "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/serial" - "v2ray.com/core/proxy" ) const ( @@ -37,7 +36,7 @@ const ( type ServerSession struct { config *ServerConfig - meta *proxy.InboundHandlerMeta + port v2net.Port } func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { @@ -178,7 +177,7 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol addr = v2net.LocalHostIP } responseAddress = addr - responsePort = s.meta.Port + responsePort = s.port } if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil { return nil, err diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 748b59928..bd1745441 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -3,7 +3,6 @@ package socks import ( "context" "io" - "sync" "time" "v2ray.com/core/app" @@ -23,16 +22,9 @@ import ( // Server is a SOCKS 5 proxy server type Server struct { - tcpMutex sync.RWMutex - udpMutex sync.RWMutex - accepting bool packetDispatcher dispatcher.Interface config *ServerConfig - tcpListener *internet.TCPHub - udpHub *udp.Hub - udpAddress net.Destination udpServer *udp.Server - meta *proxy.InboundHandlerMeta } // NewServer creates a new Server object. @@ -41,135 +33,89 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { if space == nil { return nil, errors.New("Socks|Server: No space in context.") } - meta := proxy.InboundMetaFromContext(ctx) - if meta == nil { - return nil, errors.New("Socks|Server: No inbound meta in context.") - } s := &Server{ config: config, - meta: meta, } space.OnInitialize(func() error { s.packetDispatcher = dispatcher.FromSpace(space) if s.packetDispatcher == nil { return errors.New("Socks|Server: Dispatcher is not found in the space.") } + s.udpServer = udp.NewServer(s.packetDispatcher) return nil }) return s, nil } -func (v *Server) Network() net.NetworkList { +func (s *Server) Network() net.NetworkList { list := net.NetworkList{ Network: []net.Network{net.Network_TCP}, } - if v.config.UdpEnabled { + if s.config.UdpEnabled { list.Network = append(list.Network, net.Network_UDP) } return list } -// Port implements InboundHandler.Port(). -func (v *Server) Port() net.Port { - return v.meta.Port -} - -// Close implements InboundHandler.Close(). -func (v *Server) Close() { - v.accepting = false - if v.tcpListener != nil { - v.tcpMutex.Lock() - v.tcpListener.Close() - v.tcpListener = nil - v.tcpMutex.Unlock() - } - if v.udpHub != nil { - v.udpMutex.Lock() - v.udpHub.Close() - v.udpHub = nil - v.udpMutex.Unlock() +func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection) error { + switch network { + case net.Network_TCP: + return s.processTCP(ctx, conn) + case net.Network_UDP: + return s.handleUDPPayload(ctx, conn) + default: + return errors.New("Socks|Server: Unknown network: ", network) } } -// Start implements InboundHandler.Start(). -func (v *Server) Start() error { - if v.accepting { - return nil - } +func (s *Server) processTCP(ctx context.Context, conn internet.Connection) error { + conn.SetReusable(false) - listener, err := internet.ListenTCP( - v.meta.Address, - v.meta.Port, - v.handleConnection, - v.meta.StreamSettings) - if err != nil { - log.Error("Socks: failed to listen on ", v.meta.Address, ":", v.meta.Port, ": ", err) - return err - } - v.accepting = true - v.tcpMutex.Lock() - v.tcpListener = listener - v.tcpMutex.Unlock() - if v.config.UdpEnabled { - if err := v.listenUDP(); err != nil { - return err - } - } - return nil -} - -func (v *Server) handleConnection(connection internet.Connection) { - defer connection.Close() - - connection.SetReusable(false) - - timedReader := net.NewTimeOutReader(16 /* seconds, for handshake */, connection) + timedReader := net.NewTimeOutReader(16 /* seconds, for handshake */, conn) reader := bufio.NewReader(timedReader) + inboundDest := proxy.InboundDestinationFromContext(ctx) session := &ServerSession{ - config: v.config, - meta: v.meta, + config: s.config, + port: inboundDest.Port, } - clientAddr := net.DestinationFromAddr(connection.RemoteAddr()) - - request, err := session.Handshake(reader, connection) + source := proxy.SourceFromContext(ctx) + request, err := session.Handshake(reader, conn) if err != nil { - log.Access(clientAddr, "", log.AccessRejected, err) + log.Access(source, "", log.AccessRejected, err) log.Info("Socks|Server: Failed to read request: ", err) - return + return err } if request.Command == protocol.RequestCommandTCP { dest := request.Destination() - session := &proxy.SessionInfo{ - Source: clientAddr, - Destination: dest, - Inbound: v.meta, - } log.Info("Socks|Server: TCP Connect request to ", dest) - log.Access(clientAddr, dest, log.AccessAccepted, "") + log.Access(source, dest, log.AccessAccepted, "") - timedReader.SetTimeOut(v.config.Timeout) - v.transport(reader, connection, session) - return + timedReader.SetTimeOut(s.config.Timeout) + ctx = proxy.ContextWithDestination(ctx, dest) + return s.transport(ctx, reader, conn) } if request.Command == protocol.RequestCommandUDP { - v.handleUDP() - return + return s.handleUDP() } + + return nil } -func (v *Server) handleUDP() { +func (*Server) handleUDP() error { // The TCP connection closes after v method returns. We need to wait until // the client closes it. // TODO: get notified from UDP part <-time.After(5 * time.Minute) + + return nil } -func (v *Server) transport(reader io.Reader, writer io.Writer, session *proxy.SessionInfo) { - ray := v.packetDispatcher.DispatchToOutbound(session) +func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer) error { + ray := v.packetDispatcher.DispatchToOutbound(ctx) input := ray.InboundInput() output := ray.InboundOutput() @@ -198,6 +144,48 @@ func (v *Server) transport(reader io.Reader, writer io.Writer, session *proxy.Se log.Info("Socks|Server: Connection ends with ", err) input.CloseError() output.CloseError() + return err + } + + return nil +} + +func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection) error { + source := proxy.SourceFromContext(ctx) + log.Info("Socks|Server: Client UDP connection from ", source) + + reader := buf.NewReader(conn) + for { + payload, err := reader.Read() + if err != nil { + return err + } + request, data, err := DecodeUDPPacket(payload.Bytes()) + + if err != nil { + log.Info("Socks|Server: Failed to parse UDP request: ", err) + continue + } + + if len(data) == 0 { + continue + } + + log.Info("Socks: Send packet to ", request.Destination(), " with ", len(data), " bytes") + log.Access(source, request.Destination, log.AccessAccepted, "") + + dataBuf := buf.NewSmall() + dataBuf.Append(data) + v.udpServer.Dispatch(ctx, request.Destination(), dataBuf, func(payload *buf.Buffer) { + defer payload.Release() + + log.Info("Socks|Server: Writing back UDP response with ", payload.Len(), " bytes") + + udpMessage := EncodeUDPPacket(request, payload.Bytes()) + defer udpMessage.Release() + + conn.Write(udpMessage.Bytes()) + }) } } diff --git a/proxy/socks/server_udp.go b/proxy/socks/server_udp.go deleted file mode 100644 index 75344aa53..000000000 --- a/proxy/socks/server_udp.go +++ /dev/null @@ -1,66 +0,0 @@ -package socks - -import ( - "v2ray.com/core/common/buf" - "v2ray.com/core/common/log" - "v2ray.com/core/common/net" - "v2ray.com/core/proxy" - "v2ray.com/core/transport/internet/udp" -) - -func (v *Server) listenUDP() error { - v.udpServer = udp.NewServer(v.packetDispatcher) - udpHub, err := udp.ListenUDP(v.meta.Address, v.meta.Port, udp.ListenOption{Callback: v.handleUDPPayload}) - if err != nil { - log.Error("Socks: Failed to listen on udp (", v.meta.Address, ":", v.meta.Port, "): ", err) - return err - } - v.udpMutex.Lock() - v.udpAddress = net.UDPDestination(v.config.GetNetAddress(), v.meta.Port) - v.udpHub = udpHub - v.udpMutex.Unlock() - return nil -} - -func (v *Server) handleUDPPayload(payload *buf.Buffer, session *proxy.SessionInfo) { - defer payload.Release() - - source := session.Source - log.Info("Socks: Client UDP connection from ", source) - request, data, err := DecodeUDPPacket(payload.Bytes()) - - if err != nil { - log.Error("Socks|Server: Failed to parse UDP request: ", err) - return - } - - if len(data) == 0 { - return - } - - log.Info("Socks: Send packet to ", request.Destination(), " with ", len(data), " bytes") - log.Access(source, request.Destination, log.AccessAccepted, "") - - dataBuf := buf.NewSmall() - dataBuf.Append(data) - v.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: request.Destination(), Inbound: v.meta}, dataBuf, func(destination net.Destination, payload *buf.Buffer) { - defer payload.Release() - - log.Info("Socks: Writing back UDP response with ", payload.Len(), " bytes to ", destination) - - udpMessage := EncodeUDPPacket(request, payload.Bytes()) - defer udpMessage.Release() - - v.udpMutex.RLock() - if !v.accepting { - v.udpMutex.RUnlock() - return - } - nBytes, err := v.udpHub.WriteTo(udpMessage.Bytes(), destination) - v.udpMutex.RUnlock() - - if err != nil { - log.Warning("Socks: failed to write UDP message (", nBytes, " bytes) to ", destination, ": ", err) - } - }) -} diff --git a/proxy/vmess/inbound/command.go b/proxy/vmess/inbound/command.go deleted file mode 100644 index 737cf5db6..000000000 --- a/proxy/vmess/inbound/command.go +++ /dev/null @@ -1,38 +0,0 @@ -package inbound - -import ( - "v2ray.com/core/common/log" - "v2ray.com/core/common/protocol" - "v2ray.com/core/proxy/vmess" -) - -func (v *VMessInboundHandler) generateCommand(request *protocol.RequestHeader) protocol.ResponseCommand { - if v.detours != nil { - tag := v.detours.To - if v.inboundHandlerManager != nil { - handler, availableMin := v.inboundHandlerManager.GetHandler(tag) - inboundHandler, ok := handler.(*VMessInboundHandler) - if ok { - if availableMin > 255 { - availableMin = 255 - } - - log.Info("VMessIn: Pick detour handler for port ", inboundHandler.Port(), " for ", availableMin, " minutes.") - user := inboundHandler.GetUser(request.User.Email) - if user == nil { - return nil - } - account, _ := user.GetTypedAccount() - return &protocol.CommandSwitchAccount{ - Port: inboundHandler.Port(), - ID: account.(*vmess.InternalAccount).ID.UUID(), - AlterIds: uint16(len(account.(*vmess.InternalAccount).AlterIDs)), - Level: user.Level, - ValidMin: byte(availableMin), - } - } - } - } - - return nil -} diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index a1054ca47..826cdfbdd 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -77,10 +77,7 @@ type VMessInboundHandler struct { inboundHandlerManager proxyman.InboundHandlerManager clients protocol.UserValidator usersByEmail *userByEmail - accepting bool - listener *internet.TCPHub detours *DetourConfig - meta *proxy.InboundHandlerMeta } func New(ctx context.Context, config *Config) (*VMessInboundHandler, error) { @@ -88,10 +85,6 @@ func New(ctx context.Context, config *Config) (*VMessInboundHandler, error) { if space == nil { return nil, errors.New("VMess|Inbound: No space in context.") } - meta := proxy.InboundMetaFromContext(ctx) - if meta == nil { - return nil, errors.New("VMess|Inbound: No inbound meta in context.") - } allowedClients := vmess.NewTimedUserValidator(protocol.DefaultIDHash) for _, user := range config.User { @@ -102,7 +95,6 @@ func New(ctx context.Context, config *Config) (*VMessInboundHandler, error) { clients: allowedClients, detours: config.Detour, usersByEmail: NewUserByEmail(config.User, config.GetDefaultValue()), - meta: meta, } space.OnInitialize(func() error { @@ -120,36 +112,16 @@ func New(ctx context.Context, config *Config) (*VMessInboundHandler, error) { return handler, nil } -func (v *VMessInboundHandler) Network() net.NetworkList { +func (*VMessInboundHandler) Network() net.NetworkList { return net.NetworkList{ Network: []net.Network{net.Network_TCP}, } } -func (v *VMessInboundHandler) Port() net.Port { - return v.meta.Port -} - -func (v *VMessInboundHandler) Close() { - v.Lock() - v.accepting = false - if v.listener != nil { - v.listener.Close() - v.listener = nil - v.clients.Release() - v.clients = nil - } - v.Unlock() -} - func (v *VMessInboundHandler) GetUser(email string) *protocol.User { v.RLock() defer v.RUnlock() - if !v.accepting { - return nil - } - user, existing := v.usersByEmail.Get(email) if !existing { v.clients.Add(user) @@ -157,23 +129,6 @@ func (v *VMessInboundHandler) GetUser(email string) *protocol.User { return user } -func (v *VMessInboundHandler) Start() error { - if v.accepting { - return nil - } - - tcpListener, err := internet.ListenTCP(v.meta.Address, v.meta.Port, v.HandleConnection, v.meta.StreamSettings) - if err != nil { - log.Error("VMess|Inbound: Unable to listen tcp ", v.meta.Address, ":", v.meta.Port, ": ", err) - return err - } - v.accepting = true - v.Lock() - v.listener = tcpListener - v.Unlock() - return nil -} - func transferRequest(session *encoding.ServerSession, request *protocol.RequestHeader, input io.Reader, output ray.OutputStream) error { defer output.Close() @@ -219,23 +174,12 @@ func transferResponse(session *encoding.ServerSession, request *protocol.Request return nil } -func (v *VMessInboundHandler) HandleConnection(connection internet.Connection) { - defer connection.Close() - - if !v.accepting { - return - } - +func (v *VMessInboundHandler) Process(ctx context.Context, network net.Network, connection internet.Connection) error { connReader := net.NewTimeOutReader(8, connection) reader := bufio.NewReader(connReader) - v.RLock() - if !v.accepting { - v.RUnlock() - return - } + session := encoding.NewServerSession(v.clients) request, err := session.DecodeRequestHeader(reader) - v.RUnlock() if err != nil { if errors.Cause(err) != io.EOF { @@ -243,19 +187,17 @@ func (v *VMessInboundHandler) HandleConnection(connection internet.Connection) { log.Info("VMess|Inbound: Invalid request from ", connection.RemoteAddr(), ": ", err) } connection.SetReusable(false) - return + return err } log.Access(connection.RemoteAddr(), request.Destination(), log.AccessAccepted, "") log.Info("VMess|Inbound: Received request for ", request.Destination()) connection.SetReusable(request.Option.Has(protocol.RequestOptionConnectionReuse)) - ray := v.packetDispatcher.DispatchToOutbound(&proxy.SessionInfo{ - Source: net.DestinationFromAddr(connection.RemoteAddr()), - Destination: request.Destination(), - User: request.User, - Inbound: v.meta, - }) + ctx = proxy.ContextWithDestination(ctx, request.Destination()) + ctx = protocol.ContextWithUser(ctx, request.User) + ray := v.packetDispatcher.DispatchToOutbound(ctx) + input := ray.InboundInput() output := ray.InboundOutput() @@ -269,7 +211,7 @@ func (v *VMessInboundHandler) HandleConnection(connection internet.Connection) { writer := bufio.NewWriter(connection) response := &protocol.ResponseHeader{ - Command: v.generateCommand(request), + Command: v.generateCommand(ctx, request), } if connection.Reusable() { @@ -285,14 +227,52 @@ func (v *VMessInboundHandler) HandleConnection(connection internet.Connection) { connection.SetReusable(false) input.CloseError() output.CloseError() - return + return err } if err := writer.Flush(); err != nil { log.Info("VMess|Inbound: Failed to flush remain data: ", err) connection.SetReusable(false) - return + return err } + + return nil +} + +func (v *VMessInboundHandler) generateCommand(ctx context.Context, request *protocol.RequestHeader) protocol.ResponseCommand { + if v.detours != nil { + tag := v.detours.To + if v.inboundHandlerManager != nil { + handler, err := v.inboundHandlerManager.GetHandler(ctx, tag) + if err != nil { + log.Warning("VMess|Inbound: Failed to get detour handler: ", tag, err) + return nil + } + proxyHandler, port, availableMin := handler.GetRandomInboundProxy() + inboundHandler, ok := proxyHandler.(*VMessInboundHandler) + if ok { + if availableMin > 255 { + availableMin = 255 + } + + log.Info("VMessIn: Pick detour handler for port ", port, " for ", availableMin, " minutes.") + user := inboundHandler.GetUser(request.User.Email) + if user == nil { + return nil + } + account, _ := user.GetTypedAccount() + return &protocol.CommandSwitchAccount{ + Port: port, + ID: account.(*vmess.InternalAccount).ID.UUID(), + AlterIds: uint16(len(account.(*vmess.InternalAccount).AlterIDs)), + Level: user.Level, + ValidMin: byte(availableMin), + } + } + } + } + + return nil } func init() { diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index e6420e556..bd9e88ba6 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -25,7 +25,6 @@ import ( type VMessOutboundHandler struct { serverList *protocol.ServerList serverPicker protocol.ServerPicker - meta *proxy.OutboundHandlerMeta } func New(ctx context.Context, config *Config) (*VMessOutboundHandler, error) { @@ -33,10 +32,6 @@ func New(ctx context.Context, config *Config) (*VMessOutboundHandler, error) { if space == nil { return nil, errors.New("VMess|Outbound: No space in context.") } - meta := proxy.OutboundMetaFromContext(ctx) - if meta == nil { - return nil, errors.New("VMess|Outbound: No outbound meta in context.") - } serverList := protocol.NewServerList() for _, rec := range config.Receiver { @@ -45,20 +40,20 @@ func New(ctx context.Context, config *Config) (*VMessOutboundHandler, error) { handler := &VMessOutboundHandler{ serverList: serverList, serverPicker: protocol.NewRoundRobinServerPicker(serverList), - meta: meta, } return handler, nil } // Dispatch implements OutboundHandler.Dispatch(). -func (v *VMessOutboundHandler) Dispatch(target net.Destination, outboundRay ray.OutboundRay) { +func (v *VMessOutboundHandler) Process(ctx context.Context, outboundRay ray.OutboundRay) error { var rec *protocol.ServerSpec var conn internet.Connection + dialer := proxy.DialerFromContext(ctx) err := retry.ExponentialBackoff(5, 100).On(func() error { rec = v.serverPicker.PickServer() - rawConn, err := internet.Dial(v.meta.Address, rec.Destination(), v.meta.GetDialerOptions()) + rawConn, err := dialer.Dial(ctx, rec.Destination()) if err != nil { return err } @@ -68,8 +63,11 @@ func (v *VMessOutboundHandler) Dispatch(target net.Destination, outboundRay ray. }) if err != nil { log.Warning("VMess|Outbound: Failed to find an available destination:", err) - return + return err } + defer conn.Close() + + target := proxy.DestinationFromContext(ctx) log.Info("VMess|Outbound: Tunneling request to ", target, " via ", rec.Destination()) command := protocol.RequestCommandTCP @@ -88,12 +86,11 @@ func (v *VMessOutboundHandler) Dispatch(target net.Destination, outboundRay ray. rawAccount, err := request.User.GetTypedAccount() if err != nil { log.Warning("VMess|Outbound: Failed to get user account: ", err) + return err } account := rawAccount.(*vmess.InternalAccount) request.Security = account.Security - defer conn.Close() - conn.SetReusable(true) if conn.Reusable() { // Conn reuse may be disabled on transportation layer request.Option.Set(protocol.RequestOptionConnectionReuse) @@ -160,9 +157,10 @@ func (v *VMessOutboundHandler) Dispatch(target net.Destination, outboundRay ray. conn.SetReusable(false) input.CloseError() output.CloseError() + return err } - return + return nil } func init() { diff --git a/testing/scenarios/dokodemo_test.go b/testing/scenarios/dokodemo_test.go index 97b08e85b..603832da8 100644 --- a/testing/scenarios/dokodemo_test.go +++ b/testing/scenarios/dokodemo_test.go @@ -5,6 +5,7 @@ import ( "testing" "v2ray.com/core" + "v2ray.com/core/app/proxyman" v2net "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/serial" @@ -32,11 +33,13 @@ func TestDokodemoTCP(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -47,9 +50,9 @@ func TestDokodemoTCP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } @@ -57,11 +60,13 @@ func TestDokodemoTCP(t *testing.T) { clientPort := uint32(pickPort()) clientPortRange := uint32(5) clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: &v2net.PortRange{From: clientPort, To: clientPort + clientPortRange}, - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: &v2net.PortRange{From: clientPort, To: clientPort + clientPortRange}, + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -70,9 +75,9 @@ func TestDokodemoTCP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -129,11 +134,13 @@ func TestDokodemoUDP(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -144,9 +151,9 @@ func TestDokodemoUDP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } @@ -154,11 +161,13 @@ func TestDokodemoUDP(t *testing.T) { clientPort := uint32(pickPort()) clientPortRange := uint32(5) clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: &v2net.PortRange{From: clientPort, To: clientPort + clientPortRange}, - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: &v2net.PortRange{From: clientPort, To: clientPort + clientPortRange}, + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -167,9 +176,9 @@ func TestDokodemoUDP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), diff --git a/testing/scenarios/feature_test.go b/testing/scenarios/feature_test.go index f605dcf0a..612985d8c 100644 --- a/testing/scenarios/feature_test.go +++ b/testing/scenarios/feature_test.go @@ -5,6 +5,7 @@ import ( "testing" "v2ray.com/core" + "v2ray.com/core/app/proxyman" "v2ray.com/core/app/router" v2net "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" @@ -34,12 +35,14 @@ func TestPassiveConnection(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - AllowPassiveConnection: true, - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + AllowPassiveConnection: true, + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -48,9 +51,9 @@ func TestPassiveConnection(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } @@ -103,11 +106,13 @@ func TestProxy(t *testing.T) { serverUserID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -118,9 +123,9 @@ func TestProxy(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } @@ -128,11 +133,13 @@ func TestProxy(t *testing.T) { proxyUserID := protocol.NewID(uuid.New()) proxyPort := pickPort() proxyConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(proxyPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(proxyPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -143,20 +150,22 @@ func TestProxy(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(clientPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -165,9 +174,9 @@ func TestProxy(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -182,13 +191,15 @@ func TestProxy(t *testing.T) { }, }, }), - ProxySettings: &internet.ProxyConfig{ - Tag: "proxy", - }, + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + ProxySettings: &internet.ProxyConfig{ + Tag: "proxy", + }, + }), }, { Tag: "proxy", - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -244,11 +255,16 @@ func TestProxyOverKCP(t *testing.T) { serverUserID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_MKCP, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -257,14 +273,11 @@ func TestProxyOverKCP(t *testing.T) { }, }, }), - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_MKCP, - }, }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } @@ -272,11 +285,13 @@ func TestProxyOverKCP(t *testing.T) { proxyUserID := protocol.NewID(uuid.New()) proxyPort := pickPort() proxyConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(proxyPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(proxyPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -287,23 +302,27 @@ func TestProxyOverKCP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_MKCP, - }, + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_MKCP, + }, + }), }, }, } clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(clientPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -312,9 +331,9 @@ func TestProxyOverKCP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -329,16 +348,18 @@ func TestProxyOverKCP(t *testing.T) { }, }, }), - ProxySettings: &internet.ProxyConfig{ - Tag: "proxy", - }, - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_MKCP, - }, + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + ProxySettings: &internet.ProxyConfig{ + Tag: "proxy", + }, + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_MKCP, + }, + }), }, { Tag: "proxy", - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -401,11 +422,13 @@ func TestBlackhole(t *testing.T) { serverPort := pickPort() serverPort2 := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -414,9 +437,11 @@ func TestBlackhole(t *testing.T) { }), }, { - PortRange: v2net.SinglePortRange(serverPort2), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort2), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest2.Address), Port: uint32(dest2.Port), NetworkList: &v2net.NetworkList{ @@ -425,14 +450,14 @@ func TestBlackhole(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Tag: "direct", - Settings: serial.ToTypedMessage(&freedom.Config{}), + Tag: "direct", + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, { - Tag: "blocked", - Settings: serial.ToTypedMessage(&blackhole.Config{}), + Tag: "blocked", + ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, }, App: []*serial.TypedMessage{ diff --git a/testing/scenarios/socks_test.go b/testing/scenarios/socks_test.go index 79d861cca..e0177977e 100644 --- a/testing/scenarios/socks_test.go +++ b/testing/scenarios/socks_test.go @@ -7,6 +7,7 @@ import ( xproxy "golang.org/x/net/proxy" socks4 "h12.me/socks" "v2ray.com/core" + "v2ray.com/core/app/proxyman" v2net "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/serial" @@ -30,11 +31,13 @@ func TestSocksBridgeTCP(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&socks.ServerConfig{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_PASSWORD, Accounts: map[string]string{ "Test Account": "Test Password", @@ -44,20 +47,22 @@ func TestSocksBridgeTCP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(clientPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -66,9 +71,9 @@ func TestSocksBridgeTCP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&socks.ClientConfig{ + ProxySettings: serial.ToTypedMessage(&socks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -123,11 +128,13 @@ func TestSocksBridageUDP(t *testing.T) { serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&socks.ServerConfig{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_PASSWORD, Accounts: map[string]string{ "Test Account": "Test Password", @@ -137,20 +144,22 @@ func TestSocksBridageUDP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(clientPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -159,9 +168,9 @@ func TestSocksBridageUDP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&socks.ClientConfig{ + ProxySettings: serial.ToTypedMessage(&socks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -217,11 +226,13 @@ func TestSocksConformance(t *testing.T) { authPort := pickPort() noAuthPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(authPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&socks.ServerConfig{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(authPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_PASSWORD, Accounts: map[string]string{ "Test Account": "Test Password", @@ -231,9 +242,11 @@ func TestSocksConformance(t *testing.T) { }), }, { - PortRange: v2net.SinglePortRange(noAuthPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&socks.ServerConfig{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(noAuthPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_NO_AUTH, Accounts: map[string]string{ "Test Account": "Test Password", @@ -243,9 +256,9 @@ func TestSocksConformance(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } diff --git a/testing/scenarios/tls_test.go b/testing/scenarios/tls_test.go index fbbf7f4cd..ba903748c 100644 --- a/testing/scenarios/tls_test.go +++ b/testing/scenarios/tls_test.go @@ -6,6 +6,7 @@ import ( "time" "v2ray.com/core" + "v2ray.com/core/app/proxyman" v2net "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/serial" @@ -35,11 +36,21 @@ func TestSimpleTLSConnection(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()}, + }), + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -48,30 +59,24 @@ func TestSimpleTLSConnection(t *testing.T) { }, }, }), - StreamSettings: &internet.StreamConfig{ - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()}, - }), - }, - }, }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(clientPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -80,9 +85,9 @@ func TestSimpleTLSConnection(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -97,14 +102,16 @@ func TestSimpleTLSConnection(t *testing.T) { }, }, }), - StreamSettings: &internet.StreamConfig{ - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - AllowInsecure: true, - }), + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + AllowInsecure: true, + }), + }, }, - }, + }), }, }, } @@ -143,11 +150,22 @@ func TestTLSOverKCP(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_MKCP, + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()}, + }), + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -156,31 +174,24 @@ func TestTLSOverKCP(t *testing.T) { }, }, }), - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_MKCP, - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()}, - }), - }, - }, }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(clientPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -189,9 +200,9 @@ func TestTLSOverKCP(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -206,15 +217,17 @@ func TestTLSOverKCP(t *testing.T) { }, }, }), - StreamSettings: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_MKCP, - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - AllowInsecure: true, - }), + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_MKCP, + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + AllowInsecure: true, + }), + }, }, - }, + }), }, }, } @@ -253,11 +266,21 @@ func TestTLSConnectionReuse(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()}, + }), + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -266,30 +289,24 @@ func TestTLSConnectionReuse(t *testing.T) { }, }, }), - StreamSettings: &internet.StreamConfig{ - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()}, - }), - }, - }, }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(clientPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -298,9 +315,9 @@ func TestTLSConnectionReuse(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -315,14 +332,16 @@ func TestTLSConnectionReuse(t *testing.T) { }, }, }), - StreamSettings: &internet.StreamConfig{ - SecurityType: serial.GetMessageType(&tls.Config{}), - SecuritySettings: []*serial.TypedMessage{ - serial.ToTypedMessage(&tls.Config{ - AllowInsecure: true, - }), + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + AllowInsecure: true, + }), + }, }, - }, + }), }, }, } diff --git a/testing/scenarios/transport_test.go b/testing/scenarios/transport_test.go index 2726fcac7..ab517f599 100644 --- a/testing/scenarios/transport_test.go +++ b/testing/scenarios/transport_test.go @@ -6,6 +6,7 @@ import ( "time" "v2ray.com/core" + "v2ray.com/core/app/proxyman" v2net "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/serial" @@ -35,11 +36,23 @@ func TestNoOpConnectionHeader(t *testing.T) { userID := protocol.NewID(uuid.New()) serverPort := pickPort() serverConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(serverPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&inbound.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(serverPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + TransportSettings: []*internet.TransportConfig{ + { + Protocol: internet.TransportProtocol_TCP, + Settings: serial.ToTypedMessage(&tcptransport.Config{ + HeaderSettings: serial.ToTypedMessage(&http.Config{}), + }), + }, + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ @@ -48,32 +61,24 @@ func TestNoOpConnectionHeader(t *testing.T) { }, }, }), - StreamSettings: &internet.StreamConfig{ - TransportSettings: []*internet.TransportConfig{ - { - Protocol: internet.TransportProtocol_TCP, - Settings: serial.ToTypedMessage(&tcptransport.Config{ - HeaderSettings: serial.ToTypedMessage(&http.Config{}), - }), - }, - }, - }, }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&freedom.Config{}), + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := pickPort() clientConfig := &core.Config{ - Inbound: []*core.InboundConnectionConfig{ + Inbound: []*proxyman.InboundHandlerConfig{ { - PortRange: v2net.SinglePortRange(clientPort), - ListenOn: v2net.NewIPOrDomain(v2net.LocalHostIP), - Settings: serial.ToTypedMessage(&dokodemo.Config{ + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: v2net.SinglePortRange(clientPort), + Listen: v2net.NewIPOrDomain(v2net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: v2net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &v2net.NetworkList{ @@ -82,9 +87,9 @@ func TestNoOpConnectionHeader(t *testing.T) { }), }, }, - Outbound: []*core.OutboundConnectionConfig{ + Outbound: []*proxyman.OutboundHandlerConfig{ { - Settings: serial.ToTypedMessage(&outbound.Config{ + ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: v2net.NewIPOrDomain(v2net.LocalHostIP), @@ -99,16 +104,18 @@ func TestNoOpConnectionHeader(t *testing.T) { }, }, }), - StreamSettings: &internet.StreamConfig{ - TransportSettings: []*internet.TransportConfig{ - { - Protocol: internet.TransportProtocol_TCP, - Settings: serial.ToTypedMessage(&tcptransport.Config{ - HeaderSettings: serial.ToTypedMessage(&http.Config{}), - }), + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + TransportSettings: []*internet.TransportConfig{ + { + Protocol: internet.TransportProtocol_TCP, + Settings: serial.ToTypedMessage(&tcptransport.Config{ + HeaderSettings: serial.ToTypedMessage(&http.Config{}), + }), + }, }, }, - }, + }), }, }, } diff --git a/tools/conf/router_test.go b/tools/conf/router_test.go index f7d69c2c6..5849c7928 100644 --- a/tools/conf/router_test.go +++ b/tools/conf/router_test.go @@ -4,6 +4,8 @@ import ( "net" "testing" + "context" + v2net "v2ray.com/core/common/net" "v2ray.com/core/proxy" "v2ray.com/core/testing/assert" @@ -28,22 +30,12 @@ func TestChinaIPJson(t *testing.T) { assert.String(rule.Tag).Equals("x") cond, err := rule.BuildCondition() assert.Error(err).IsNil() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("121.14.1.189"), 80), - })).IsTrue() // sina.com.cn - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("101.226.103.106"), 80), - })).IsTrue() // qq.com - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("115.239.210.36"), 80), - })).IsTrue() // image.baidu.com - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("120.135.126.1"), 80), - })).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("121.14.1.189"), 80)))).IsTrue() // sina.com.cn + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("101.226.103.106"), 80)))).IsTrue() // qq.com + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("115.239.210.36"), 80)))).IsTrue() // image.baidu.com + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("120.135.126.1"), 80)))).IsTrue() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("8.8.8.8"), 80), - })).IsFalse() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("8.8.8.8"), 80)))).IsFalse() } func TestChinaSitesJson(t *testing.T) { @@ -56,22 +48,12 @@ func TestChinaSitesJson(t *testing.T) { assert.String(rule.Tag).Equals("y") cond, err := rule.BuildCondition() assert.Error(err).IsNil() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("v.qq.com"), 80), - })).IsTrue() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("www.163.com"), 80), - })).IsTrue() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("ngacn.cc"), 80), - })).IsTrue() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("12306.cn"), 80), - })).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("v.qq.com"), 80)))).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.163.com"), 80)))).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("ngacn.cc"), 80)))).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("12306.cn"), 80)))).IsTrue() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("v2ray.com"), 80), - })).IsFalse() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("v2ray.com"), 80)))).IsFalse() } func TestDomainRule(t *testing.T) { @@ -90,21 +72,11 @@ func TestDomainRule(t *testing.T) { assert.Pointer(rule).IsNotNil() cond, err := rule.BuildCondition() assert.Error(err).IsNil() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("www.ooxx.com"), 80), - })).IsTrue() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("www.aabb.com"), 80), - })).IsFalse() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80), - })).IsFalse() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("www.12306.cn"), 80), - })).IsTrue() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.ParseAddress("www.acn.com"), 80), - })).IsFalse() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.ooxx.com"), 80)))).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.aabb.com"), 80)))).IsFalse() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80)))).IsFalse() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.12306.cn"), 80)))).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.acn.com"), 80)))).IsFalse() } func TestIPRule(t *testing.T) { @@ -122,18 +94,10 @@ func TestIPRule(t *testing.T) { assert.Pointer(rule).IsNotNil() cond, err := rule.BuildCondition() assert.Error(err).IsNil() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80), - })).IsFalse() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80), - })).IsTrue() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80), - })).IsFalse() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Destination: v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80), - })).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80)))).IsFalse() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80)))).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80)))).IsFalse() + assert.Bool(cond.Apply(proxy.ContextWithDestination(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80)))).IsTrue() } func TestSourceIPRule(t *testing.T) { @@ -150,16 +114,8 @@ func TestSourceIPRule(t *testing.T) { assert.Pointer(rule).IsNotNil() cond, err := rule.BuildCondition() assert.Error(err).IsNil() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Source: v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80), - })).IsFalse() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Source: v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80), - })).IsTrue() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Source: v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80), - })).IsFalse() - assert.Bool(cond.Apply(&proxy.SessionInfo{ - Source: v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80), - })).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithSource(context.Background(), v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80)))).IsFalse() + assert.Bool(cond.Apply(proxy.ContextWithSource(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80)))).IsTrue() + assert.Bool(cond.Apply(proxy.ContextWithSource(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80)))).IsFalse() + assert.Bool(cond.Apply(proxy.ContextWithSource(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80)))).IsTrue() } diff --git a/tools/conf/v2ray.go b/tools/conf/v2ray.go index ba5bb10f4..61bb1bd97 100644 --- a/tools/conf/v2ray.go +++ b/tools/conf/v2ray.go @@ -6,6 +6,7 @@ import ( "strings" "v2ray.com/core" + "v2ray.com/core/app/proxyman" "v2ray.com/core/common/errors" v2net "v2ray.com/core/common/net" "v2ray.com/core/common/serial" @@ -40,40 +41,45 @@ type InboundConnectionConfig struct { Tag string `json:"tag"` } -func (v *InboundConnectionConfig) Build() (*core.InboundConnectionConfig, error) { - config := new(core.InboundConnectionConfig) - config.PortRange = &v2net.PortRange{ - From: uint32(v.Port), - To: uint32(v.Port), +func (v *InboundConnectionConfig) Build() (*proxyman.InboundHandlerConfig, error) { + receiverConfig := &proxyman.ReceiverConfig{ + PortRange: &v2net.PortRange{ + From: uint32(v.Port), + To: uint32(v.Port), + }, + AllowPassiveConnection: v.AllowPassive, } if v.Listen != nil { if v.Listen.Family().IsDomain() { return nil, errors.New("Point: Unable to listen on domain address: " + v.Listen.Domain()) } - config.ListenOn = v.Listen.Build() + receiverConfig.Listen = v.Listen.Build() } if v.StreamSetting != nil { ts, err := v.StreamSetting.Build() if err != nil { return nil, err } - config.StreamSettings = ts + receiverConfig.StreamSettings = ts } - config.AllowPassiveConnection = v.AllowPassive jsonConfig, err := inboundConfigLoader.LoadWithID(v.Settings, v.Protocol) if err != nil { return nil, errors.Base(err).Message("Failed to load inbound config.") } + if dokodemoConfig, ok := jsonConfig.(*DokodemoConfig); ok { + receiverConfig.ReceiveOriginalDestination = dokodemoConfig.Redirect + } ts, err := jsonConfig.(Buildable).Build() if err != nil { return nil, err } - config.Settings = ts - if len(v.Tag) > 0 { - config.Tag = v.Tag - } - return config, nil + + return &proxyman.InboundHandlerConfig{ + Tag: v.Tag, + ReceiverSettings: serial.ToTypedMessage(receiverConfig), + ProxySettings: ts, + }, nil } type OutboundConnectionConfig struct { @@ -85,8 +91,31 @@ type OutboundConnectionConfig struct { Tag string `json:"tag"` } -func (v *OutboundConnectionConfig) Build() (*core.OutboundConnectionConfig, error) { - config := new(core.OutboundConnectionConfig) +func (v *OutboundConnectionConfig) Build() (*proxyman.OutboundHandlerConfig, error) { + senderSettings := &proxyman.SenderConfig{} + + if v.SendThrough != nil { + address := v.SendThrough + if address.Family().IsDomain() { + return nil, errors.New("Invalid sendThrough address: " + address.String()) + } + senderSettings.Via = address.Build() + } + if v.StreamSetting != nil { + ss, err := v.StreamSetting.Build() + if err != nil { + return nil, err + } + senderSettings.StreamSettings = ss + } + if v.ProxySettings != nil { + ps, err := v.ProxySettings.Build() + if err != nil { + return nil, errors.Base(err).Message("Invalid outbound proxy settings.") + } + senderSettings.ProxySettings = ps + } + rawConfig, err := outboundConfigLoader.LoadWithID(v.Settings, v.Protocol) if err != nil { return nil, errors.Base(err).Message("Failed to parse outbound config.") @@ -95,33 +124,12 @@ func (v *OutboundConnectionConfig) Build() (*core.OutboundConnectionConfig, erro if err != nil { return nil, err } - config.Settings = ts - if v.SendThrough != nil { - address := v.SendThrough - if address.Family().IsDomain() { - return nil, errors.New("Invalid sendThrough address: " + address.String()) - } - config.SendThrough = address.Build() - } - if v.StreamSetting != nil { - ss, err := v.StreamSetting.Build() - if err != nil { - return nil, err - } - config.StreamSettings = ss - } - if v.ProxySettings != nil { - ps, err := v.ProxySettings.Build() - if err != nil { - return nil, errors.Base(err).Message("Invalid outbound proxy settings.") - } - config.ProxySettings = ps - } - if len(v.Tag) > 0 { - config.Tag = v.Tag - } - return config, nil + return &proxyman.OutboundHandlerConfig{ + SenderSettings: serial.ToTypedMessage(senderSettings), + ProxySettings: ts, + Tag: v.Tag, + }, nil } type InboundDetourAllocationConfig struct { @@ -130,26 +138,26 @@ type InboundDetourAllocationConfig struct { RefreshMin *uint32 `json:"refresh"` } -func (v *InboundDetourAllocationConfig) Build() (*core.AllocationStrategy, error) { - config := new(core.AllocationStrategy) +func (v *InboundDetourAllocationConfig) Build() (*proxyman.AllocationStrategy, error) { + config := new(proxyman.AllocationStrategy) switch strings.ToLower(v.Strategy) { case "always": - config.Type = core.AllocationStrategy_Always + config.Type = proxyman.AllocationStrategy_Always case "random": - config.Type = core.AllocationStrategy_Random + config.Type = proxyman.AllocationStrategy_Random case "external": - config.Type = core.AllocationStrategy_External + config.Type = proxyman.AllocationStrategy_External default: return nil, errors.New("Unknown allocation strategy: ", v.Strategy) } if v.Concurrency != nil { - config.Concurrency = &core.AllocationStrategyConcurrency{ + config.Concurrency = &proxyman.AllocationStrategy_AllocationStrategyConcurrency{ Value: *v.Concurrency, } } if v.RefreshMin != nil { - config.Refresh = &core.AllocationStrategyRefresh{ + config.Refresh = &proxyman.AllocationStrategy_AllocationStrategyRefresh{ Value: *v.RefreshMin, } } @@ -168,46 +176,54 @@ type InboundDetourConfig struct { AllowPassive bool `json:"allowPassive"` } -func (v *InboundDetourConfig) Build() (*core.InboundConnectionConfig, error) { - config := new(core.InboundConnectionConfig) +func (v *InboundDetourConfig) Build() (*proxyman.InboundHandlerConfig, error) { + receiverSettings := &proxyman.ReceiverConfig{ + AllowPassiveConnection: v.AllowPassive, + } + if v.PortRange == nil { return nil, errors.New("Port range not specified in InboundDetour.") } - config.PortRange = v.PortRange.Build() + receiverSettings.PortRange = v.PortRange.Build() if v.ListenOn != nil { if v.ListenOn.Family().IsDomain() { return nil, errors.New("Unable to listen on domain address: ", v.ListenOn.Domain()) } - config.ListenOn = v.ListenOn.Build() + receiverSettings.Listen = v.ListenOn.Build() } - config.Tag = v.Tag if v.Allocation != nil { as, err := v.Allocation.Build() if err != nil { return nil, err } - config.AllocationStrategy = as + receiverSettings.AllocationStrategy = as } if v.StreamSetting != nil { ss, err := v.StreamSetting.Build() if err != nil { return nil, err } - config.StreamSettings = ss + receiverSettings.StreamSettings = ss } - config.AllowPassiveConnection = v.AllowPassive rawConfig, err := inboundConfigLoader.LoadWithID(v.Settings, v.Protocol) if err != nil { return nil, errors.Base(err).Message("Failed to load inbound detour config.") } + if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok { + receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect + } ts, err := rawConfig.(Buildable).Build() if err != nil { return nil, err } - config.Settings = ts - return config, nil + + return &proxyman.InboundHandlerConfig{ + Tag: v.Tag, + ReceiverSettings: serial.ToTypedMessage(receiverSettings), + ProxySettings: ts, + }, nil } type OutboundDetourConfig struct { @@ -219,16 +235,15 @@ type OutboundDetourConfig struct { ProxySettings *ProxyConfig `json:"proxySettings"` } -func (v *OutboundDetourConfig) Build() (*core.OutboundConnectionConfig, error) { - config := new(core.OutboundConnectionConfig) - config.Tag = v.Tag +func (v *OutboundDetourConfig) Build() (*proxyman.OutboundHandlerConfig, error) { + senderSettings := &proxyman.SenderConfig{} if v.SendThrough != nil { address := v.SendThrough if address.Family().IsDomain() { return nil, errors.New("Point: Unable to send through: " + address.String()) } - config.SendThrough = address.Build() + senderSettings.Via = address.Build() } if v.StreamSetting != nil { @@ -236,7 +251,15 @@ func (v *OutboundDetourConfig) Build() (*core.OutboundConnectionConfig, error) { if err != nil { return nil, err } - config.StreamSettings = ss + senderSettings.StreamSettings = ss + } + + if v.ProxySettings != nil { + ps, err := v.ProxySettings.Build() + if err != nil { + return nil, errors.Base(err).Message("Invalid outbound detour proxy settings.") + } + senderSettings.ProxySettings = ps } rawConfig, err := outboundConfigLoader.LoadWithID(v.Settings, v.Protocol) @@ -248,15 +271,11 @@ func (v *OutboundDetourConfig) Build() (*core.OutboundConnectionConfig, error) { return nil, err } - if v.ProxySettings != nil { - ps, err := v.ProxySettings.Build() - if err != nil { - return nil, errors.Base(err).Message("Invalid outbound detour proxy settings.") - } - config.ProxySettings = ps - } - config.Settings = ts - return config, nil + return &proxyman.OutboundHandlerConfig{ + SenderSettings: serial.ToTypedMessage(senderSettings), + Tag: v.Tag, + ProxySettings: ts, + }, nil } type Config struct { diff --git a/transport/internet/config.go b/transport/internet/config.go index e8807364d..d3d83c1dc 100644 --- a/transport/internet/config.go +++ b/transport/internet/config.go @@ -56,6 +56,24 @@ func (v *StreamConfig) GetEffectiveTransportSettings() (interface{}, error) { return CreateTransportConfig(protocol) } +func (c *StreamConfig) GetTransportSettingsFor(protocol TransportProtocol) (interface{}, error) { + if c != nil { + for _, settings := range c.TransportSettings { + if settings.Protocol == protocol { + return settings.GetTypedSettings() + } + } + } + + for _, settings := range globalTransportSettings { + if settings.Protocol == protocol { + return settings.GetTypedSettings() + } + } + + return CreateTransportConfig(protocol) +} + func (v *StreamConfig) GetEffectiveSecuritySettings() (interface{}, error) { for _, settings := range v.SecuritySettings { if settings.Type == v.SecurityType { diff --git a/transport/internet/context.go b/transport/internet/context.go new file mode 100644 index 000000000..33f8b8624 --- /dev/null +++ b/transport/internet/context.go @@ -0,0 +1,52 @@ +package internet + +import ( + "context" + + "v2ray.com/core/common/net" +) + +type key int + +const ( + streamSettingsKey key = iota + dialerSrcKey + transportSettingsKey + securitySettingsKey +) + +func ContextWithStreamSettings(ctx context.Context, streamSettings *StreamConfig) context.Context { + return context.WithValue(ctx, streamSettingsKey, streamSettings) +} + +func StreamSettingsFromContext(ctx context.Context) (*StreamConfig, bool) { + ss, ok := ctx.Value(streamSettingsKey).(*StreamConfig) + return ss, ok +} + +func ContextWithDialerSource(ctx context.Context, addr net.Address) context.Context { + return context.WithValue(ctx, dialerSrcKey, addr) +} + +func DialerSourceFromContext(ctx context.Context) net.Address { + if addr, ok := ctx.Value(dialerSrcKey).(net.Address); ok { + return addr + } + return net.AnyIP +} + +func ContextWithTransportSettings(ctx context.Context, transportSettings interface{}) context.Context { + return context.WithValue(ctx, transportSettingsKey, transportSettings) +} + +func TransportSettingsFromContext(ctx context.Context) interface{} { + return ctx.Value(transportSettingsKey) +} + +func ContextWithSecuritySettings(ctx context.Context, securitySettings interface{}) context.Context { + return context.WithValue(ctx, securitySettingsKey, securitySettings) +} + +func SecuritySettingsFromContext(ctx context.Context) interface{} { + return ctx.Value(securitySettingsKey) +} diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index 60689404f..1452a8435 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -1,24 +1,17 @@ package internet import ( + "context" "net" "v2ray.com/core/common/errors" - "v2ray.com/core/common/log" v2net "v2ray.com/core/common/net" ) -type DialerOptions struct { - Stream *StreamConfig - Proxy *ProxyConfig -} - -type Dialer func(src v2net.Address, dest v2net.Destination, options DialerOptions) (Connection, error) +type Dialer func(ctx context.Context, dest v2net.Destination) (Connection, error) var ( transportDialerCache = make(map[TransportProtocol]Dialer) - - ProxyDialer Dialer ) func RegisterTransportDialer(protocol TransportProtocol, dialer Dialer) error { @@ -29,26 +22,34 @@ func RegisterTransportDialer(protocol TransportProtocol, dialer Dialer) error { return nil } -func Dial(src v2net.Address, dest v2net.Destination, options DialerOptions) (Connection, error) { - if options.Proxy.HasTag() && ProxyDialer != nil { - log.Info("Internet: Proxying outbound connection through: ", options.Proxy.Tag) - return ProxyDialer(src, dest, options) - } - +func Dial(ctx context.Context, dest v2net.Destination) (Connection, error) { if dest.Network == v2net.Network_TCP { - protocol := options.Stream.GetEffectiveProtocol() + streamSettings, _ := StreamSettingsFromContext(ctx) + protocol := streamSettings.GetEffectiveProtocol() + transportSettings, err := streamSettings.GetEffectiveTransportSettings() + if err != nil { + return nil, err + } + ctx = ContextWithTransportSettings(ctx, transportSettings) + if streamSettings != nil && streamSettings.HasSecuritySettings() { + securitySettings, err := streamSettings.GetEffectiveSecuritySettings() + if err != nil { + return nil, err + } + ctx = ContextWithSecuritySettings(ctx, securitySettings) + } dialer := transportDialerCache[protocol] if dialer == nil { - return nil, errors.New("Internet|Dialer: ", options.Stream.Protocol, " dialer not registered.") + return nil, errors.New("Internet|Dialer: ", protocol, " dialer not registered.") } - return dialer(src, dest, options) + return dialer(ctx, dest) } udpDialer := transportDialerCache[TransportProtocol_UDP] if udpDialer == nil { return nil, errors.New("Internet|Dialer: UDP dialer not registered.") } - return udpDialer(src, dest, options) + return udpDialer(ctx, dest) } // DialSystem calls system dialer to create a network connection. diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go index 254aea627..f90efc1a3 100644 --- a/transport/internet/kcp/dialer.go +++ b/transport/internet/kcp/dialer.go @@ -1,6 +1,7 @@ package kcp import ( + "context" "crypto/cipher" "crypto/tls" "net" @@ -108,10 +109,11 @@ func (o *ClientConnection) Run() { } } -func DialKCP(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (internet.Connection, error) { +func DialKCP(ctx context.Context, dest v2net.Destination) (internet.Connection, error) { dest.Network = v2net.Network_UDP log.Info("KCP|Dialer: Dialing KCP to ", dest) + src := internet.DialerSourceFromContext(ctx) id := internal.NewConnectionID(src, dest) conn := globalPool.Get(id) if conn == nil { @@ -128,12 +130,7 @@ func DialKCP(src v2net.Address, dest v2net.Destination, options internet.DialerO conn = c } - networkSettings, err := options.Stream.GetEffectiveTransportSettings() - if err != nil { - log.Error("KCP|Dialer: Failed to get KCP settings: ", err) - return nil, err - } - kcpSettings := networkSettings.(*Config) + kcpSettings := internet.TransportSettingsFromContext(ctx).(*Config) clientConn := conn.(*ClientConnection) header, err := kcpSettings.GetPackerHeader() @@ -151,12 +148,7 @@ func DialKCP(src v2net.Address, dest v2net.Destination, options internet.DialerO var iConn internet.Connection iConn = session - if options.Stream != nil && options.Stream.HasSecuritySettings() { - securitySettings, err := options.Stream.GetEffectiveSecuritySettings() - if err != nil { - log.Error("KCP|Dialer: Failed to get security settings: ", err) - return nil, err - } + if securitySettings := internet.SecuritySettingsFromContext(ctx); securitySettings != nil { switch securitySettings := securitySettings.(type) { case *v2tls.Config: config := securitySettings.GetTLSConfig() diff --git a/transport/internet/kcp/kcp_test.go b/transport/internet/kcp/kcp_test.go index c38107a8f..8e1048587 100644 --- a/transport/internet/kcp/kcp_test.go +++ b/transport/internet/kcp/kcp_test.go @@ -1,6 +1,7 @@ package kcp_test import ( + "context" "crypto/rand" "io" "net" @@ -55,19 +56,10 @@ func TestDialAndListen(t *testing.T) { } }() + ctx := internet.ContextWithTransportSettings(context.Background(), &Config{}) wg := new(sync.WaitGroup) for i := 0; i < 10; i++ { - clientConn, err := DialKCP(v2net.LocalHostIP, v2net.UDPDestination(v2net.LocalHostIP, port), internet.DialerOptions{ - Stream: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_MKCP, - TransportSettings: []*internet.TransportConfig{ - { - Protocol: internet.TransportProtocol_MKCP, - Settings: serial.ToTypedMessage(&Config{}), - }, - }, - }, - }) + clientConn, err := DialKCP(ctx, v2net.UDPDestination(v2net.LocalHostIP, port)) assert.Error(err).IsNil() wg.Add(1) diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index e94bfefb4..9c34ffdb0 100644 --- a/transport/internet/kcp/listener.go +++ b/transport/internet/kcp/listener.go @@ -13,7 +13,6 @@ import ( "v2ray.com/core/common/errors" "v2ray.com/core/common/log" v2net "v2ray.com/core/common/net" - "v2ray.com/core/proxy" "v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet/internal" v2tls "v2ray.com/core/transport/internet/tls" @@ -140,11 +139,9 @@ func NewListener(address v2net.Address, port v2net.Port, options internet.Listen return l, nil } -func (v *Listener) OnReceive(payload *buf.Buffer, session *proxy.SessionInfo) { +func (v *Listener) OnReceive(payload *buf.Buffer, src v2net.Destination, originalDest v2net.Destination) { defer payload.Release() - src := session.Source - segments := v.reader.Read(payload.Bytes()) if len(segments) == 0 { log.Info("KCP|Listener: discarding invalid payload from ", src) diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index 7262dae78..1bb050522 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -1,6 +1,7 @@ package tcp import ( + "context" "crypto/tls" "net" @@ -17,16 +18,11 @@ var ( globalCache = internal.NewConnectionPool() ) -func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (internet.Connection, error) { +func Dial(ctx context.Context, dest v2net.Destination) (internet.Connection, error) { log.Info("Internet|TCP: Dailing TCP to ", dest) - if src == nil { - src = v2net.AnyIP - } - networkSettings, err := options.Stream.GetEffectiveTransportSettings() - if err != nil { - return nil, err - } - tcpSettings := networkSettings.(*Config) + src := internet.DialerSourceFromContext(ctx) + + tcpSettings := internet.TransportSettingsFromContext(ctx).(*Config) id := internal.NewConnectionID(src, dest) var conn net.Conn @@ -39,12 +35,7 @@ func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOpti if err != nil { return nil, err } - if options.Stream != nil && options.Stream.HasSecuritySettings() { - securitySettings, err := options.Stream.GetEffectiveSecuritySettings() - if err != nil { - log.Error("TCP: Failed to get security settings: ", err) - return nil, err - } + if securitySettings := internet.SecuritySettingsFromContext(ctx); securitySettings != nil { tlsConfig, ok := securitySettings.(*v2tls.Config) if ok { config := tlsConfig.GetTLSConfig() diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index cfba22872..8075936a7 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -42,6 +42,7 @@ func ListenTCP(address v2net.Address, port v2net.Port, options internet.ListenOp if err != nil { return nil, err } + log.Info("TCP|Listener: Listening on ", address, ":", port) networkSettings, err := options.Stream.GetEffectiveTransportSettings() if err != nil { return nil, err diff --git a/proxy/dokodemo/sockopt_linux.go b/transport/internet/tcp/sockopt_linux.go similarity index 98% rename from proxy/dokodemo/sockopt_linux.go rename to transport/internet/tcp/sockopt_linux.go index 342b903e6..679e08971 100644 --- a/proxy/dokodemo/sockopt_linux.go +++ b/transport/internet/tcp/sockopt_linux.go @@ -1,6 +1,6 @@ // +build linux -package dokodemo +package tcp import ( "syscall" diff --git a/proxy/dokodemo/sockopt_other.go b/transport/internet/tcp/sockopt_other.go similarity index 92% rename from proxy/dokodemo/sockopt_other.go rename to transport/internet/tcp/sockopt_other.go index 55c316c49..3c0fbf109 100644 --- a/proxy/dokodemo/sockopt_other.go +++ b/transport/internet/tcp/sockopt_other.go @@ -1,6 +1,6 @@ // +build !linux -package dokodemo +package tcp import ( "v2ray.com/core/common/net" diff --git a/transport/internet/tcp_hub.go b/transport/internet/tcp_hub.go index 3a76ebd66..b689a26ea 100644 --- a/transport/internet/tcp_hub.go +++ b/transport/internet/tcp_hub.go @@ -24,7 +24,8 @@ func RegisterTransportListener(protocol TransportProtocol, listener ListenFunc) type ListenFunc func(address v2net.Address, port v2net.Port, options ListenOptions) (Listener, error) type ListenOptions struct { - Stream *StreamConfig + Stream *StreamConfig + RecvOrigDest bool } type Listener interface { diff --git a/transport/internet/udp/dialer.go b/transport/internet/udp/dialer.go index 0b9f3aed4..1f940fbab 100644 --- a/transport/internet/udp/dialer.go +++ b/transport/internet/udp/dialer.go @@ -1,6 +1,8 @@ package udp import ( + "context" + "v2ray.com/core/common" v2net "v2ray.com/core/common/net" "v2ray.com/core/transport/internet" @@ -9,7 +11,8 @@ import ( func init() { common.Must(internet.RegisterTransportDialer(internet.TransportProtocol_UDP, - func(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (internet.Connection, error) { + func(ctx context.Context, dest v2net.Destination) (internet.Connection, error) { + src := internet.DialerSourceFromContext(ctx) conn, err := internet.DialSystem(src, dest) if err != nil { return nil, err diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index 05913907c..11eeda56e 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -9,18 +9,18 @@ import ( "v2ray.com/core/common/log" v2net "v2ray.com/core/common/net" "v2ray.com/core/common/signal" - "v2ray.com/core/proxy" "v2ray.com/core/transport/internet/internal" ) // Payload represents a single UDP payload. type Payload struct { - payload *buf.Buffer - session *proxy.SessionInfo + payload *buf.Buffer + source v2net.Destination + originalDest v2net.Destination } // PayloadHandler is function to handle Payload. -type PayloadHandler func(*buf.Buffer, *proxy.SessionInfo) +type PayloadHandler func(payload *buf.Buffer, source v2net.Destination, originalDest v2net.Destination) // PayloadQueue is a queue of Payload. type PayloadQueue struct { @@ -59,7 +59,7 @@ func (v *PayloadQueue) Enqueue(payload Payload) { func (v *PayloadQueue) Dequeue(queue <-chan Payload) { for payload := range queue { - v.callback(payload.payload, payload.session) + v.callback(payload.payload, payload.source, payload.originalDest) } } @@ -94,6 +94,7 @@ func ListenUDP(address v2net.Address, port v2net.Port, option ListenOption) (*Hu if err != nil { return nil, err } + log.Info("UDP|Hub: Listening on ", address, ":", port) if option.ReceiveOriginalDest { fd, err := internal.GetSysFd(udpConn) if err != nil { @@ -155,15 +156,14 @@ func (v *Hub) start() { continue } - session := new(proxy.SessionInfo) - session.Source = v2net.UDPDestination(v2net.IPAddress(addr.IP), v2net.Port(addr.Port)) - if v.option.ReceiveOriginalDest && noob > 0 { - session.Destination = RetrieveOriginalDest(oobBytes[:noob]) - } - v.queue.Enqueue(Payload{ + payload := Payload{ payload: buffer, - session: session, - }) + } + payload.source = v2net.UDPDestination(v2net.IPAddress(addr.IP), v2net.Port(addr.Port)) + if v.option.ReceiveOriginalDest && noob > 0 { + payload.originalDest = RetrieveOriginalDest(oobBytes[:noob]) + } + v.queue.Enqueue(payload) } } diff --git a/transport/internet/udp/udp_server.go b/transport/internet/udp/udp_server.go index 25f5cf640..781d2fff8 100644 --- a/transport/internet/udp/udp_server.go +++ b/transport/internet/udp/udp_server.go @@ -1,8 +1,8 @@ package udp import ( + "context" "sync" - "time" "v2ray.com/core/app/dispatcher" "v2ray.com/core/common/buf" @@ -12,95 +12,17 @@ import ( "v2ray.com/core/transport/ray" ) -type ResponseCallback func(destination v2net.Destination, payload *buf.Buffer) - -type TimedInboundRay struct { - name string - inboundRay ray.InboundRay - accessed chan bool - server *Server - sync.RWMutex -} - -func NewTimedInboundRay(name string, inboundRay ray.InboundRay, server *Server) *TimedInboundRay { - r := &TimedInboundRay{ - name: name, - inboundRay: inboundRay, - accessed: make(chan bool, 1), - server: server, - } - go r.Monitor() - return r -} - -func (v *TimedInboundRay) Monitor() { - for { - time.Sleep(time.Second * 16) - select { - case <-v.accessed: - default: - // Ray not accessed for a while, assuming communication is dead. - v.RLock() - if v.server == nil { - v.RUnlock() - return - } - v.server.RemoveRay(v.name) - v.RUnlock() - v.Release() - return - } - } -} - -func (v *TimedInboundRay) InboundInput() ray.OutputStream { - v.RLock() - defer v.RUnlock() - if v.inboundRay == nil { - return nil - } - select { - case v.accessed <- true: - default: - } - return v.inboundRay.InboundInput() -} - -func (v *TimedInboundRay) InboundOutput() ray.InputStream { - v.RLock() - defer v.RUnlock() - if v.inboundRay == nil { - return nil - } - select { - case v.accessed <- true: - default: - } - return v.inboundRay.InboundOutput() -} - -func (v *TimedInboundRay) Release() { - log.Debug("UDP Server: Releasing TimedInboundRay: ", v.name) - v.Lock() - defer v.Unlock() - if v.server == nil { - return - } - v.server = nil - v.inboundRay.InboundInput().Close() - v.inboundRay.InboundOutput().CloseError() - v.inboundRay = nil -} +type ResponseCallback func(payload *buf.Buffer) type Server struct { sync.RWMutex - conns map[string]*TimedInboundRay + conns map[string]ray.InboundRay packetDispatcher dispatcher.Interface } func NewServer(packetDispatcher dispatcher.Interface) *Server { return &Server{ - conns: make(map[string]*TimedInboundRay), + conns: make(map[string]ray.InboundRay), packetDispatcher: packetDispatcher, } } @@ -108,69 +30,53 @@ func NewServer(packetDispatcher dispatcher.Interface) *Server { func (v *Server) RemoveRay(name string) { v.Lock() defer v.Unlock() - delete(v.conns, name) -} - -func (v *Server) locateExistingAndDispatch(name string, payload *buf.Buffer) bool { - log.Debug("UDP Server: Locating existing connection for ", name) - v.RLock() - defer v.RUnlock() - if entry, found := v.conns[name]; found { - outputStream := entry.InboundInput() - if outputStream == nil { - return false - } - err := outputStream.Write(payload) - if err != nil { - go entry.Release() - return false - } - return true + if conn, found := v.conns[name]; found { + conn.InboundInput().Close() + conn.InboundOutput().Close() + delete(v.conns, name) } - return false } -func (v *Server) getInboundRay(dest string, session *proxy.SessionInfo) (*TimedInboundRay, bool) { +func (v *Server) getInboundRay(ctx context.Context, dest v2net.Destination) (ray.InboundRay, bool) { + destString := dest.String() v.Lock() defer v.Unlock() - if entry, found := v.conns[dest]; found { + if entry, found := v.conns[destString]; found { return entry, true } log.Info("UDP|Server: establishing new connection for ", dest) - inboundRay := v.packetDispatcher.DispatchToOutbound(session) - return NewTimedInboundRay(dest, inboundRay, v), false + ctx = proxy.ContextWithDestination(ctx, dest) + return v.packetDispatcher.DispatchToOutbound(ctx), false } -func (v *Server) Dispatch(session *proxy.SessionInfo, payload *buf.Buffer, callback ResponseCallback) { - source := session.Source - destination := session.Destination - +func (v *Server) Dispatch(ctx context.Context, destination v2net.Destination, payload *buf.Buffer, callback ResponseCallback) { // TODO: Add user to destString - destString := source.String() + "-" + destination.String() + destString := destination.String() log.Debug("UDP|Server: Dispatch request: ", destString) - inboundRay, existing := v.getInboundRay(destString, session) + + inboundRay, existing := v.getInboundRay(ctx, destination) outputStream := inboundRay.InboundInput() if outputStream != nil { - outputStream.Write(payload) + if err := outputStream.Write(payload); err != nil { + v.RemoveRay(destString) + } } if !existing { - go v.handleConnection(inboundRay, source, callback) + go func() { + handleInput(inboundRay.InboundOutput(), callback) + v.RemoveRay(destString) + }() } } -func (v *Server) handleConnection(inboundRay *TimedInboundRay, source v2net.Destination, callback ResponseCallback) { +func handleInput(input ray.InputStream, callback ResponseCallback) { for { - inputStream := inboundRay.InboundOutput() - if inputStream == nil { - break - } - data, err := inputStream.Read() + data, err := input.Read() if err != nil { break } - callback(source, data) + callback(data) } - inboundRay.Release() } diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index 330725acf..7c93a7796 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -1,6 +1,7 @@ package websocket import ( + "context" "io/ioutil" "net" @@ -17,16 +18,10 @@ var ( globalCache = internal.NewConnectionPool() ) -func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (internet.Connection, error) { - log.Info("WebSocket|Dailer: Creating connection to ", dest) - if src == nil { - src = v2net.AnyIP - } - networkSettings, err := options.Stream.GetEffectiveTransportSettings() - if err != nil { - return nil, err - } - wsSettings := networkSettings.(*Config) +func Dial(ctx context.Context, dest v2net.Destination) (internet.Connection, error) { + log.Info("WebSocket|Dialer: Creating connection to ", dest) + src := internet.DialerSourceFromContext(ctx) + wsSettings := internet.TransportSettingsFromContext(ctx).(*Config) id := internal.NewConnectionID(src, dest) var conn *wsconn @@ -38,7 +33,7 @@ func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOpti } if conn == nil { var err error - conn, err = wsDial(src, dest, options) + conn, err = wsDial(ctx, dest) if err != nil { log.Warning("WebSocket|Dialer: Dial failed: ", err) return nil, err @@ -51,12 +46,9 @@ func init() { common.Must(internet.RegisterTransportDialer(internet.TransportProtocol_WebSocket, Dial)) } -func wsDial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (*wsconn, error) { - networkSettings, err := options.Stream.GetEffectiveTransportSettings() - if err != nil { - return nil, err - } - wsSettings := networkSettings.(*Config) +func wsDial(ctx context.Context, dest v2net.Destination) (*wsconn, error) { + src := internet.DialerSourceFromContext(ctx) + wsSettings := internet.TransportSettingsFromContext(ctx).(*Config) commonDial := func(network, addr string) (net.Conn, error) { return internet.DialSystem(src, dest) @@ -70,15 +62,10 @@ func wsDial(src v2net.Address, dest v2net.Destination, options internet.DialerOp protocol := "ws" - if options.Stream != nil && options.Stream.HasSecuritySettings() { - protocol = "wss" - securitySettings, err := options.Stream.GetEffectiveSecuritySettings() - if err != nil { - log.Error("WebSocket: Failed to create security settings: ", err) - return nil, err - } + if securitySettings := internet.SecuritySettingsFromContext(ctx); securitySettings != nil { tlsConfig, ok := securitySettings.(*v2tls.Config) if ok { + protocol = "wss" dialer.TLSClientConfig = tlsConfig.GetTLSConfig() if dest.Address.Family().IsDomain() { dialer.TLSClientConfig.ServerName = dest.Address.Domain() diff --git a/transport/internet/websocket/ws_test.go b/transport/internet/websocket/ws_test.go index 162eb93a8..c351cd39c 100644 --- a/transport/internet/websocket/ws_test.go +++ b/transport/internet/websocket/ws_test.go @@ -6,6 +6,8 @@ import ( "bytes" + "context" + v2net "v2ray.com/core/common/net" "v2ray.com/core/common/serial" "v2ray.com/core/testing/assert" @@ -54,19 +56,10 @@ func Test_listenWSAndDial(t *testing.T) { }() } }() - conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13146), internet.DialerOptions{ - Stream: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_WebSocket, - TransportSettings: []*internet.TransportConfig{ - { - Protocol: internet.TransportProtocol_WebSocket, - Settings: serial.ToTypedMessage(&Config{ - Path: "ws", - }), - }, - }, - }, - }) + + ctx := internet.ContextWithTransportSettings(context.Background(), &Config{Path: "ws"}) + conn, err := Dial(ctx, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13146)) + assert.Error(err).IsNil() _, err = conn.Write([]byte("Test connection 1")) assert.Error(err).IsNil() @@ -78,19 +71,7 @@ func Test_listenWSAndDial(t *testing.T) { assert.Error(conn.Close()).IsNil() <-time.After(time.Second * 5) - conn, err = Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13146), internet.DialerOptions{ - Stream: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_WebSocket, - TransportSettings: []*internet.TransportConfig{ - { - Protocol: internet.TransportProtocol_WebSocket, - Settings: serial.ToTypedMessage(&Config{ - Path: "ws", - }), - }, - }, - }, - }) + conn, err = Dial(ctx, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13146)) assert.Error(err).IsNil() _, err = conn.Write([]byte("Test connection 2")) assert.Error(err).IsNil() @@ -99,19 +80,7 @@ func Test_listenWSAndDial(t *testing.T) { assert.String(string(b[:n])).Equals("Response") assert.Error(conn.Close()).IsNil() <-time.After(time.Second * 15) - conn, err = Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13146), internet.DialerOptions{ - Stream: &internet.StreamConfig{ - Protocol: internet.TransportProtocol_WebSocket, - TransportSettings: []*internet.TransportConfig{ - { - Protocol: internet.TransportProtocol_WebSocket, - Settings: serial.ToTypedMessage(&Config{ - Path: "ws", - }), - }, - }, - }, - }) + conn, err = Dial(ctx, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13146)) assert.Error(err).IsNil() _, err = conn.Write([]byte("Test connection 3")) assert.Error(err).IsNil() @@ -157,26 +126,16 @@ func Test_listenWSAndDial_TLS(t *testing.T) { conn.Close() listen.Close() }() - conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13143), internet.DialerOptions{ - Stream: &internet.StreamConfig{ - SecurityType: serial.GetMessageType(new(v2tls.Config)), - SecuritySettings: []*serial.TypedMessage{serial.ToTypedMessage(&v2tls.Config{ - AllowInsecure: true, - })}, - Protocol: internet.TransportProtocol_WebSocket, - TransportSettings: []*internet.TransportConfig{ - { - Protocol: internet.TransportProtocol_WebSocket, - Settings: serial.ToTypedMessage(&Config{ - Path: "wss", - ConnectionReuse: &ConnectionReuse{ - Enable: true, - }, - }), - }, - }, + ctx := internet.ContextWithTransportSettings(context.Background(), &Config{ + Path: "wss", + ConnectionReuse: &ConnectionReuse{ + Enable: true, }, }) + ctx = internet.ContextWithSecuritySettings(ctx, &v2tls.Config{ + AllowInsecure: true, + }) + conn, err := Dial(ctx, v2net.TCPDestination(v2net.DomainAddress("localhost"), 13143)) assert.Error(err).IsNil() conn.Close() } diff --git a/transport/ray/direct.go b/transport/ray/direct.go index 1acc39fae..21628d46e 100644 --- a/transport/ray/direct.go +++ b/transport/ray/direct.go @@ -6,6 +6,8 @@ import ( "time" + "context" + "v2ray.com/core/common/buf" ) @@ -16,10 +18,10 @@ const ( var ErrReadTimeout = errors.New("Ray: timeout.") // NewRay creates a new Ray for direct traffic transport. -func NewRay() Ray { +func NewRay(ctx context.Context) Ray { return &directRay{ - Input: NewStream(), - Output: NewStream(), + Input: NewStream(ctx), + Output: NewStream(ctx), } } @@ -54,13 +56,15 @@ func (v *directRay) AddInspector(inspector Inspector) { type Stream struct { buffer chan *buf.Buffer + ctx context.Context close chan bool err chan bool inspector *InspectorChain } -func NewStream() *Stream { +func NewStream(ctx context.Context) *Stream { return &Stream{ + ctx: ctx, buffer: make(chan *buf.Buffer, bufferSize), close: make(chan bool), err: make(chan bool), @@ -70,12 +74,16 @@ func NewStream() *Stream { func (v *Stream) Read() (*buf.Buffer, error) { select { + case <-v.ctx.Done(): + return nil, io.ErrClosedPipe case <-v.err: return nil, io.ErrClosedPipe case b := <-v.buffer: return b, nil default: select { + case <-v.ctx.Done(): + return nil, io.ErrClosedPipe case b := <-v.buffer: return b, nil case <-v.close: @@ -88,12 +96,16 @@ func (v *Stream) Read() (*buf.Buffer, error) { func (v *Stream) ReadTimeout(timeout time.Duration) (*buf.Buffer, error) { select { + case <-v.ctx.Done(): + return nil, io.ErrClosedPipe case <-v.err: return nil, io.ErrClosedPipe case b := <-v.buffer: return b, nil default: select { + case <-v.ctx.Done(): + return nil, io.ErrClosedPipe case b := <-v.buffer: return b, nil case <-v.close: @@ -112,12 +124,16 @@ func (v *Stream) Write(data *buf.Buffer) (err error) { } select { + case <-v.ctx.Done(): + return io.ErrClosedPipe case <-v.err: return io.ErrClosedPipe case <-v.close: return io.ErrClosedPipe default: select { + case <-v.ctx.Done(): + return io.ErrClosedPipe case <-v.err: return io.ErrClosedPipe case <-v.close: diff --git a/transport/ray/direct_test.go b/transport/ray/direct_test.go index 637f801a4..eb8d7d9c6 100644 --- a/transport/ray/direct_test.go +++ b/transport/ray/direct_test.go @@ -4,6 +4,8 @@ import ( "io" "testing" + "context" + "v2ray.com/core/common/buf" "v2ray.com/core/testing/assert" . "v2ray.com/core/transport/ray" @@ -12,7 +14,7 @@ import ( func TestStreamIO(t *testing.T) { assert := assert.On(t) - stream := NewStream() + stream := NewStream(context.Background()) b1 := buf.New() b1.AppendBytes('a') assert.Error(stream.Write(b1)).IsNil() @@ -33,7 +35,7 @@ func TestStreamIO(t *testing.T) { func TestStreamClose(t *testing.T) { assert := assert.On(t) - stream := NewStream() + stream := NewStream(context.Background()) b1 := buf.New() b1.AppendBytes('a') assert.Error(stream.Write(b1)).IsNil() diff --git a/v2ray.go b/v2ray.go index 90ea4b870..e94f0e39d 100644 --- a/v2ray.go +++ b/v2ray.go @@ -6,22 +6,13 @@ import ( "v2ray.com/core/app" "v2ray.com/core/app/dispatcher" "v2ray.com/core/app/dns" - proxydialer "v2ray.com/core/app/proxy" "v2ray.com/core/app/proxyman" - "v2ray.com/core/common" "v2ray.com/core/common/log" v2net "v2ray.com/core/common/net" - "v2ray.com/core/proxy" ) // Point shell of V2Ray. type Point struct { - inboundHandlers []InboundDetourHandler - taggedInboundHandlers map[string]InboundDetourHandler - - outboundHandlers []proxy.OutboundHandler - taggedOutboundHandlers map[string]proxy.OutboundHandler - space app.Space } @@ -42,7 +33,6 @@ func NewPoint(pConfig *Config) (*Point, error) { ctx := app.ContextWithSpace(context.Background(), space) vpoint.space = space - vpoint.space.AddApplication(vpoint) outboundHandlerManager := proxyman.OutboundHandlerManagerFromSpace(space) if outboundHandlerManager == nil { @@ -54,16 +44,15 @@ func NewPoint(pConfig *Config) (*Point, error) { outboundHandlerManager = o.(proxyman.OutboundHandlerManager) } - proxyDialer := proxydialer.OutboundProxyFromSpace(space) - if proxyDialer == nil { - p, err := app.CreateAppFromConfig(ctx, new(proxydialer.Config)) + inboundHandlerManager := proxyman.InboundHandlerManagerFromSpace(space) + if inboundHandlerManager == nil { + o, err := app.CreateAppFromConfig(ctx, new(proxyman.InboundConfig)) if err != nil { return nil, err } - space.AddApplication(p) - proxyDialer = p.(*proxydialer.OutboundProxy) + space.AddApplication(o) + inboundHandlerManager = o.(proxyman.InboundHandlerManager) } - proxyDialer.RegisterDialer() for _, appSettings := range pConfig.App { settings, err := appSettings.GetInstance() @@ -104,62 +93,16 @@ func NewPoint(pConfig *Config) (*Point, error) { disp = d.(dispatcher.Interface) } - vpoint.inboundHandlers = make([]InboundDetourHandler, 0, 8) - vpoint.taggedInboundHandlers = make(map[string]InboundDetourHandler) for _, inbound := range pConfig.Inbound { - allocConfig := inbound.GetAllocationStrategyValue() - var inboundHandler InboundDetourHandler - switch allocConfig.Type { - case AllocationStrategy_Always: - dh, err := NewInboundDetourHandlerAlways(ctx, inbound) - if err != nil { - log.Error("V2Ray: Failed to create detour handler: ", err) - return nil, common.ErrBadConfiguration - } - inboundHandler = dh - case AllocationStrategy_Random: - dh, err := NewInboundDetourHandlerDynamic(ctx, inbound) - if err != nil { - log.Error("V2Ray: Failed to create detour handler: ", err) - return nil, common.ErrBadConfiguration - } - inboundHandler = dh - default: - log.Error("V2Ray: Unknown allocation strategy: ", allocConfig.Type) - return nil, common.ErrBadConfiguration - } - vpoint.inboundHandlers = append(vpoint.inboundHandlers, inboundHandler) - if len(inbound.Tag) > 0 { - vpoint.taggedInboundHandlers[inbound.Tag] = inboundHandler + if err := inboundHandlerManager.AddHandler(ctx, inbound); err != nil { + return nil, err } } - vpoint.outboundHandlers = make([]proxy.OutboundHandler, 0, 8) - vpoint.taggedOutboundHandlers = make(map[string]proxy.OutboundHandler) - for idx, outbound := range pConfig.Outbound { - outboundSettings, err := outbound.GetTypedSettings() - if err != nil { + for _, outbound := range pConfig.Outbound { + if err := outboundHandlerManager.AddHandler(ctx, outbound); err != nil { return nil, err } - outboundHandler, err := proxy.CreateOutboundHandler(proxy.ContextWithOutboundMeta(ctx, &proxy.OutboundHandlerMeta{ - Tag: outbound.Tag, - Address: outbound.GetSendThroughValue(), - StreamSettings: outbound.StreamSettings, - ProxySettings: outbound.ProxySettings, - }), outboundSettings) - if err != nil { - log.Error("V2Ray: Failed to create detour outbound connection handler: ", err) - return nil, err - } - if idx == 0 { - outboundHandlerManager.SetDefaultHandler(outboundHandler) - } - if len(outbound.Tag) > 0 { - outboundHandlerManager.SetHandler(outbound.Tag, outboundHandler) - vpoint.taggedOutboundHandlers[outbound.Tag] = outboundHandler - } - - vpoint.outboundHandlers = append(vpoint.outboundHandlers, outboundHandler) } if err := vpoint.space.Initialize(); err != nil { @@ -169,39 +112,19 @@ func NewPoint(pConfig *Config) (*Point, error) { return vpoint, nil } -func (Point) Interface() interface{} { - return (*proxyman.InboundHandlerManager)(nil) -} - func (v *Point) Close() { - for _, inbound := range v.inboundHandlers { - inbound.Close() - } + ihm := proxyman.InboundHandlerManagerFromSpace(v.space) + ihm.Close() } // Start starts the Point server, and return any error during the process. // In the case of any errors, the state of the server is unpredicatable. func (v *Point) Start() error { - for _, inbound := range v.inboundHandlers { - err := inbound.Start() - if err != nil { - return err - } + ihm := proxyman.InboundHandlerManagerFromSpace(v.space) + if err := ihm.Start(); err != nil { + return err } log.Warning("V2Ray started.") return nil } - -func (v *Point) GetHandler(tag string) (proxy.InboundHandler, int) { - handler, found := v.taggedInboundHandlers[tag] - if !found { - log.Warning("V2Ray: Unable to find an inbound handler with tag: ", tag) - return nil, 0 - } - return handler.GetConnectionHandler() -} - -func (v *Point) Release() { - -}