Compare commits

..

80 Commits

Author SHA1 Message Date
Darien Raymond
2e0d54fd4c fix routing config on source IP 2016-11-09 10:56:15 +01:00
Darien Raymond
d343cb1ee6 proxy connection 2016-11-09 00:17:09 +01:00
Darien Raymond
7a09fcdc2f increase kcp ack frequency 2016-11-08 00:35:14 +01:00
Darien Raymond
b0d009664a Fix HTTP serialization 2016-11-07 10:47:30 +01:00
Darien Raymond
3d2431d21c update test data 2016-11-07 10:38:43 +01:00
Darien Raymond
d04e145442 Update version 2016-11-06 21:05:19 +01:00
Darien Raymond
d75cb28413 fix tcp encoding 2016-11-06 21:04:56 +01:00
Darien Raymond
9f68062d48 refine http header parsing 2016-11-06 15:48:25 +01:00
Darien Raymond
d70b997d84 test case for shadowsocks UDP 2016-11-06 14:32:04 +01:00
Darien Raymond
dc0cbce6e1 optimize pickString 2016-11-06 14:04:44 +01:00
Darien Raymond
edc5bbbb72 add more default headers 2016-11-06 13:55:06 +01:00
Darien Raymond
66e8090d3a update http header 2016-11-06 13:38:32 +01:00
Darien Raymond
805bbe5fe4 default user agent strings 2016-11-06 00:42:54 +01:00
Darien Raymond
31be091a55 simplify http header parsing 2016-11-05 15:14:55 +01:00
Darien Raymond
f108633e2e ssr compatibility 2016-11-05 01:50:51 +01:00
Darien Raymond
d2263a452d update http header json config 2016-11-05 01:28:15 +01:00
Darien Raymond
dfc03ff939 support noop connection auth 2016-11-05 01:15:32 +01:00
Darien Raymond
13dde1799d comments 2016-11-05 01:01:46 +01:00
Darien Raymond
c84629c374 add shadowsocks in json config 2016-11-05 01:01:30 +01:00
Darien Raymond
817cc8d82e fix shadowsocks conf 2016-11-05 01:01:07 +01:00
Darien Raymond
13ab2622c5 shadowsocks test 2016-11-05 01:00:54 +01:00
Darien Raymond
0727aa0da9 simplify imports 2016-11-05 01:00:20 +01:00
Darien Raymond
3a15f799c2 prevent deadlock in shadowsocks 2016-11-05 01:00:09 +01:00
Darien Raymond
bae0de7d95 test case for http auth 2016-11-04 21:59:19 +01:00
Darien Raymond
80312627c4 fix typo 2016-11-04 10:49:43 +01:00
Darien Raymond
3d167a6855 json config for http header 2016-11-04 10:49:18 +01:00
Darien Raymond
010f34c76c allow single side auth 2016-11-03 23:14:27 +01:00
Darien Raymond
0747203132 refine http header 2016-11-02 22:26:21 +01:00
Darien Raymond
1600a59254 enable tcp for shadowsocks server 2016-11-02 22:19:01 +01:00
Darien Raymond
c5a92e00ef dont reuse shadowsock connections 2016-11-02 22:18:25 +01:00
Darien Raymond
68b85cce60 remove unused code 2016-11-02 16:42:02 +01:00
Darien Raymond
9d2407f4e4 fix test break 2016-11-02 16:41:02 +01:00
Darien Raymond
cdb0debcb0 register shadowsocks client factory 2016-11-02 16:38:09 +01:00
Darien Raymond
5f3f173b5e shadowsocks client factory 2016-11-02 16:33:04 +01:00
Darien Raymond
35aa16d40d refine shadowsocks user 2016-11-02 16:22:29 +01:00
Darien Raymond
de4836c720 refine ota settings in conf 2016-11-02 16:21:20 +01:00
Darien Raymond
63d3c9fa30 typo 2016-11-02 16:18:29 +01:00
Darien Raymond
687e008c9a refine shadowsocks ota settings 2016-11-02 16:17:57 +01:00
Darien Raymond
43dacc3936 remove small buffer pool 2016-11-01 12:37:35 +01:00
Darien Raymond
aabb9137e1 remove unused code 2016-11-01 12:34:07 +01:00
Darien Raymond
33d2513e3c reduce memory allocation in kcp 2016-11-01 12:07:20 +01:00
Darien Raymond
5b58066345 use local buffer in UDP hub 2016-11-01 11:46:34 +01:00
Darien Raymond
d3f323e24b response factory 2016-11-01 00:42:55 +01:00
Darien Raymond
187688cacb fix type def 2016-11-01 00:42:44 +01:00
Darien Raymond
72339a3509 fix TCP conn reuse with tls 2016-11-01 00:41:46 +01:00
Darien Raymond
ac3b91a877 http authenticator 2016-10-31 22:26:46 +01:00
Darien Raymond
5e1c6fe816 bug fix 2016-10-31 16:46:47 +01:00
Darien Raymond
a54c39b4ac config for shadowsocks 2016-10-31 16:46:15 +01:00
Darien Raymond
c221802963 shadowsocks client 2016-10-31 16:35:18 +01:00
Darien Raymond
be4f3d0772 refine shadowsocks server 2016-10-31 15:24:28 +01:00
Darien Raymond
fbb44e7e02 fix string checking logic 2016-10-24 16:59:43 +02:00
Darien Raymond
369256c82f include user when dispatch requests in VMess. 2016-10-24 16:57:16 +02:00
Darien Raymond
531be77a59 remove test case depending on external resources 2016-10-24 16:49:41 +02:00
Darien Raymond
97dc7b30de remove test cases depending on external resources 2016-10-24 16:48:16 +02:00
Darien Raymond
360c222c1c try again to skip coverage on release 2016-10-24 16:45:02 +02:00
Darien Raymond
1fb9a911cd workaround travis issue 2016-10-24 16:42:28 +02:00
Darien Raymond
6006fd9ca7 Update version 2016-10-24 15:09:24 +02:00
Darien Raymond
59fa064cae default log settings 2016-10-24 15:08:06 +02:00
Darien Raymond
3373e62193 Merge branch 'master' of https://github.com/v2ray/v2ray-core 2016-10-24 15:04:23 +02:00
Darien Raymond
2c2c569c77 Update to Go 1.7.3 2016-10-24 14:13:21 +02:00
Darien Raymond
687ae6c50e chunk writer 2016-10-21 00:33:23 +02:00
Darien Raymond
35ba8710e0 fix ip check in ipnet 2016-10-19 15:38:31 +02:00
Darien Raymond
a7ef82ffbc fix test break 2016-10-19 12:01:11 +02:00
Darien Raymond
ca980f5718 json conf for source session in router 2016-10-18 23:14:48 +02:00
Darien Raymond
aae99a8e98 use session in router 2016-10-18 23:01:39 +02:00
Darien Raymond
f37b04a690 per connection stream settings 2016-10-18 21:57:40 +02:00
Darien Raymond
e13c97d162 rename IP to CIDR in router 2016-10-18 16:42:22 +02:00
Darien Raymond
2af4b16913 remove string list 2016-10-18 16:10:50 +02:00
Darien Raymond
2320bc3304 remove unused code 2016-10-18 15:31:48 +02:00
Darien Raymond
751a105324 comments 2016-10-18 15:31:39 +02:00
Darien Raymond
b3bbd80674 fix json parser for IPv6 routing 2016-10-18 11:36:45 +02:00
Darien Raymond
4e80ed05d9 comments 2016-10-18 10:31:39 +02:00
Darien Raymond
426a58707f skip coverage for releases 2016-10-18 10:28:37 +02:00
Darien Raymond
9b4d9cf0e7 nil pointer 2016-10-18 10:04:15 +02:00
Darien Raymond
f049b3cc2b comments 2016-10-18 00:12:09 +02:00
Darien Raymond
b81d091fb8 comments 2016-10-18 00:09:49 +02:00
Darien Raymond
9bd8822668 unused proto 2016-10-18 00:09:41 +02:00
Darien Raymond
ad59e56925 default send through value 2016-10-18 00:02:41 +02:00
Darien Raymond
4a67587873 fix geoip 2016-10-17 16:35:18 +02:00
Darien Raymond
8b936bc816 fix release binary 2016-10-17 15:52:07 +02:00
107 changed files with 2740 additions and 9127 deletions

View File

@@ -1,7 +1,7 @@
language: go
go:
- 1.7.1
- 1.7.3
go_import_path: v2ray.com/core
@@ -15,8 +15,8 @@ after_success:
- ./testing/coverage/coverall
before_deploy:
- rvm 1.9.3 do gem install mime-types -v 2.6.2
- "./tools/release/release-ci.sh"
- gem install octokit -v 4.3.0 # 4.4.0 needs Ruby 2 which is missing on the build
deploy:
provider: releases

1
all.go
View File

@@ -20,6 +20,7 @@ import (
_ "v2ray.com/core/transport/internet/udp"
_ "v2ray.com/core/transport/internet/ws"
_ "v2ray.com/core/transport/internet/authenticators/http"
_ "v2ray.com/core/transport/internet/authenticators/noop"
_ "v2ray.com/core/transport/internet/authenticators/srtp"
_ "v2ray.com/core/transport/internet/authenticators/utp"

View File

@@ -49,7 +49,7 @@ func (this *DefaultDispatcher) DispatchToOutbound(meta *proxy.InboundHandlerMeta
destination := session.Destination
if this.router != nil {
if tag, err := this.router.TakeDetour(destination); err == nil {
if tag, err := this.router.TakeDetour(session); err == nil {
if handler := this.ohm.GetHandler(tag); handler != nil {
log.Info("DefaultDispatcher: Taking detour [", tag, "] for [", destination, "].")
dispatcher = handler

View File

@@ -31,8 +31,11 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Config struct {
NameServers []*v2ray_core_common_net2.Endpoint `protobuf:"bytes,1,rep,name=NameServers" json:"NameServers,omitempty"`
Hosts map[string]*v2ray_core_common_net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
// Nameservers used by this DNS. Only traditional UDP servers are support at the moment.
// A special value 'localhost' as a domain address can be set to use DNS on local system.
NameServers []*v2ray_core_common_net2.Endpoint `protobuf:"bytes,1,rep,name=NameServers" json:"NameServers,omitempty"`
// Static hosts. Domain to IP.
Hosts map[string]*v2ray_core_common_net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
}
func (m *Config) Reset() { *m = Config{} }

View File

@@ -9,6 +9,10 @@ import "v2ray.com/core/common/net/address.proto";
import "v2ray.com/core/common/net/destination.proto";
message Config {
// Nameservers used by this DNS. Only traditional UDP servers are support at the moment.
// A special value 'localhost' as a domain address can be set to use DNS on local system.
repeated v2ray.core.common.net.Endpoint NameServers = 1;
// Static hosts. Domain to IP.
map<string, v2ray.core.common.net.IPOrDomain> Hosts = 2;
}

View File

@@ -1,56 +0,0 @@
package dns_test
import (
"net"
"testing"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
dispatchers "v2ray.com/core/app/dispatcher/impl"
. "v2ray.com/core/app/dns"
"v2ray.com/core/app/proxyman"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/proxy"
"v2ray.com/core/proxy/freedom"
"v2ray.com/core/testing/assert"
"v2ray.com/core/transport/internet"
)
func TestDnsAdd(t *testing.T) {
assert := assert.On(t)
space := app.NewSpace()
outboundHandlerManager := proxyman.NewDefaultOutboundHandlerManager()
outboundHandlerManager.SetDefaultHandler(
freedom.NewFreedomConnection(
&freedom.Config{},
space,
&proxy.OutboundHandlerMeta{
Address: v2net.AnyIP,
StreamSettings: &internet.StreamConfig{
Network: v2net.Network_RawTCP,
},
}))
space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager)
space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space))
domain := "local.v2ray.com"
server := NewCacheServer(space, &Config{
NameServers: []*v2net.Endpoint{{
Network: v2net.Network_UDP,
Address: &v2net.IPOrDomain{
Address: &v2net.IPOrDomain_Ip{
Ip: []byte{8, 8, 8, 8},
},
},
Port: 53,
}},
})
space.BindApp(APP_ID, server)
space.Initialize()
ips := server.Get(domain)
assert.Int(len(ips)).Equals(1)
assert.IP(ips[0].To4()).Equals(net.IP([]byte{127, 0, 0, 1}))
}

131
app/proxy/proxy.go Normal file
View File

@@ -0,0 +1,131 @@
package proxy
import (
"errors"
"io"
"net"
"time"
"v2ray.com/core/app"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common/alloc"
v2io "v2ray.com/core/common/io"
"v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray"
)
const (
APP_ID = 7
)
type OutboundProxy struct {
outboundManager proxyman.OutboundHandlerManager
}
func NewOutboundProxy(space app.Space) *OutboundProxy {
proxy := new(OutboundProxy)
space.InitializeApplication(func() error {
if !space.HasApp(proxyman.APP_ID_OUTBOUND_MANAGER) {
return errors.New("Proxy: Outbound handler manager not found.")
}
proxy.outboundManager = space.GetApp(proxyman.APP_ID_OUTBOUND_MANAGER).(proxyman.OutboundHandlerManager)
return nil
})
return proxy
}
func (this *OutboundProxy) Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (internet.Connection, error) {
handler := this.outboundManager.GetHandler(options.ProxyTag)
if handler == nil {
log.Warning("Proxy: Failed to get outbound handler with tag: ", options.ProxyTag)
return internet.Dial(src, dest, internet.DialerOptions{
Stream: options.Stream,
})
}
stream := ray.NewRay()
if err := handler.Dispatch(dest, alloc.NewLocalBuffer(32).Clear(), stream); err != nil {
return nil, err
}
return NewProxyConnection(src, dest, stream), nil
}
func (this *OutboundProxy) Release() {
}
type ProxyConnection struct {
stream ray.Ray
closed bool
localAddr net.Addr
remoteAddr net.Addr
reader *v2io.ChanReader
writer *v2io.ChainWriter
}
func NewProxyConnection(src v2net.Address, dest v2net.Destination, stream ray.Ray) *ProxyConnection {
return &ProxyConnection{
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: v2io.NewChanReader(stream.InboundOutput()),
writer: v2io.NewChainWriter(stream.InboundInput()),
}
}
func (this *ProxyConnection) Read(b []byte) (int, error) {
if this.closed {
return 0, io.EOF
}
return this.reader.Read(b)
}
func (this *ProxyConnection) Write(b []byte) (int, error) {
if this.closed {
return 0, io.EOF
}
return this.writer.Write(b)
}
func (this *ProxyConnection) Close() error {
this.closed = true
this.stream.InboundInput().Close()
this.stream.InboundOutput().Release()
return nil
}
func (this *ProxyConnection) LocalAddr() net.Addr {
return this.localAddr
}
func (this *ProxyConnection) RemoteAddr() net.Addr {
return this.remoteAddr
}
func (this *ProxyConnection) SetDeadline(t time.Time) error {
return nil
}
func (this *ProxyConnection) SetReadDeadline(t time.Time) error {
return nil
}
func (this *ProxyConnection) SetWriteDeadline(t time.Time) error {
return nil
}
func (this *ProxyConnection) Reusable() bool {
return false
}
func (this *ProxyConnection) SetReusable(bool) {
}

View File

@@ -6,10 +6,11 @@ import (
"strings"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/proxy"
)
type Condition interface {
Apply(dest v2net.Destination) bool
Apply(session *proxy.SessionInfo) bool
}
type ConditionChan []Condition
@@ -24,9 +25,9 @@ func (this *ConditionChan) Add(cond Condition) *ConditionChan {
return this
}
func (this *ConditionChan) Apply(dest v2net.Destination) bool {
func (this *ConditionChan) Apply(session *proxy.SessionInfo) bool {
for _, cond := range *this {
if !cond.Apply(dest) {
if !cond.Apply(session) {
return false
}
}
@@ -49,9 +50,9 @@ func (this *AnyCondition) Add(cond Condition) *AnyCondition {
return this
}
func (this *AnyCondition) Apply(dest v2net.Destination) bool {
func (this *AnyCondition) Apply(session *proxy.SessionInfo) bool {
for _, cond := range *this {
if cond.Apply(dest) {
if cond.Apply(session) {
return true
}
}
@@ -72,7 +73,8 @@ func NewPlainDomainMatcher(pattern string) *PlainDomainMatcher {
}
}
func (this *PlainDomainMatcher) Apply(dest v2net.Destination) bool {
func (this *PlainDomainMatcher) Apply(session *proxy.SessionInfo) bool {
dest := session.Destination
if !dest.Address.Family().IsDomain() {
return false
}
@@ -94,7 +96,8 @@ func NewRegexpDomainMatcher(pattern string) (*RegexpDomainMatcher, error) {
}, nil
}
func (this *RegexpDomainMatcher) Apply(dest v2net.Destination) bool {
func (this *RegexpDomainMatcher) Apply(session *proxy.SessionInfo) bool {
dest := session.Destination
if !dest.Address.Family().IsDomain() {
return false
}
@@ -103,20 +106,26 @@ func (this *RegexpDomainMatcher) Apply(dest v2net.Destination) bool {
}
type CIDRMatcher struct {
cidr *net.IPNet
cidr *net.IPNet
onSource bool
}
func NewCIDRMatcher(ip []byte, mask uint32) (*CIDRMatcher, error) {
func NewCIDRMatcher(ip []byte, mask uint32, onSource bool) (*CIDRMatcher, error) {
cidr := &net.IPNet{
IP: net.IP(ip),
Mask: net.CIDRMask(int(mask), len(ip)),
}
return &CIDRMatcher{
cidr: cidr,
cidr: cidr,
onSource: onSource,
}, nil
}
func (this *CIDRMatcher) Apply(dest v2net.Destination) bool {
func (this *CIDRMatcher) Apply(session *proxy.SessionInfo) bool {
dest := session.Destination
if this.onSource {
dest = session.Source
}
if !dest.Address.Family().Either(v2net.AddressFamilyIPv4, v2net.AddressFamilyIPv6) {
return false
}
@@ -124,16 +133,22 @@ func (this *CIDRMatcher) Apply(dest v2net.Destination) bool {
}
type IPv4Matcher struct {
ipv4net *v2net.IPNet
ipv4net *v2net.IPNet
onSource bool
}
func NewIPv4Matcher(ipnet *v2net.IPNet) *IPv4Matcher {
func NewIPv4Matcher(ipnet *v2net.IPNet, onSource bool) *IPv4Matcher {
return &IPv4Matcher{
ipv4net: ipnet,
ipv4net: ipnet,
onSource: onSource,
}
}
func (this *IPv4Matcher) Apply(dest v2net.Destination) bool {
func (this *IPv4Matcher) Apply(session *proxy.SessionInfo) bool {
dest := session.Destination
if this.onSource {
dest = session.Source
}
if !dest.Address.Family().Either(v2net.AddressFamilyIPv4) {
return false
}
@@ -150,8 +165,8 @@ func NewPortMatcher(portRange v2net.PortRange) *PortMatcher {
}
}
func (this *PortMatcher) Apply(dest v2net.Destination) bool {
return this.port.Contains(dest.Port)
func (this *PortMatcher) Apply(session *proxy.SessionInfo) bool {
return this.port.Contains(session.Destination.Port)
}
type NetworkMatcher struct {
@@ -164,6 +179,28 @@ func NewNetworkMatcher(network *v2net.NetworkList) *NetworkMatcher {
}
}
func (this *NetworkMatcher) Apply(dest v2net.Destination) bool {
return this.network.HasNetwork(dest.Network)
func (this *NetworkMatcher) Apply(session *proxy.SessionInfo) bool {
return this.network.HasNetwork(session.Destination.Network)
}
type UserMatcher struct {
user []string
}
func NewUserMatcher(users []string) *UserMatcher {
return &UserMatcher{
user: users,
}
}
func (this *UserMatcher) Apply(session *proxy.SessionInfo) bool {
if session.User == nil {
return false
}
for _, u := range this.user {
if u == session.User.Email {
return true
}
}
return false
}

View File

@@ -5,6 +5,7 @@ import (
"net"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/proxy"
)
type Rule struct {
@@ -12,8 +13,8 @@ type Rule struct {
Condition Condition
}
func (this *Rule) Apply(dest v2net.Destination) bool {
return this.Condition.Apply(dest)
func (this *Rule) Apply(session *proxy.SessionInfo) bool {
return this.Condition.Apply(session)
}
func (this *RoutingRule) BuildCondition() (Condition, error) {
@@ -35,19 +36,18 @@ func (this *RoutingRule) BuildCondition() (Condition, error) {
conds.Add(anyCond)
}
if len(this.Ip) > 0 {
ipv4Net := make(map[uint32]byte)
if len(this.Cidr) > 0 {
ipv4Net := v2net.NewIPNet()
ipv6Cond := NewAnyCondition()
hasIpv6 := false
for _, ip := range this.Ip {
for _, ip := range this.Cidr {
switch len(ip.Ip) {
case net.IPv4len:
k := (uint32(ip.Ip[0]) << 24) + (uint32(ip.Ip[1]) << 16) + (uint32(ip.Ip[2]) << 8) + uint32(ip.Ip[3])
ipv4Net[k] = byte(32 - ip.UnmatchingBits)
ipv4Net.AddIP(ip.Ip, byte(ip.Prefix))
case net.IPv6len:
hasIpv6 = true
matcher, err := NewCIDRMatcher(ip.Ip, uint32(32)-ip.UnmatchingBits)
matcher, err := NewCIDRMatcher(ip.Ip, ip.Prefix, false)
if err != nil {
return nil, err
}
@@ -57,13 +57,13 @@ func (this *RoutingRule) BuildCondition() (Condition, error) {
}
}
if len(ipv4Net) > 0 && hasIpv6 {
if !ipv4Net.IsEmpty() && hasIpv6 {
cond := NewAnyCondition()
cond.Add(NewIPv4Matcher(v2net.NewIPNetInitialValue(ipv4Net)))
cond.Add(NewIPv4Matcher(ipv4Net, false))
cond.Add(ipv6Cond)
conds.Add(cond)
} else if len(ipv4Net) > 0 {
conds.Add(NewIPv4Matcher(v2net.NewIPNetInitialValue(ipv4Net)))
} else if !ipv4Net.IsEmpty() {
conds.Add(NewIPv4Matcher(ipv4Net, false))
} else if hasIpv6 {
conds.Add(ipv6Cond)
}
@@ -77,6 +77,43 @@ func (this *RoutingRule) BuildCondition() (Condition, error) {
conds.Add(NewNetworkMatcher(this.NetworkList))
}
if len(this.SourceCidr) > 0 {
ipv4Net := v2net.NewIPNet()
ipv6Cond := NewAnyCondition()
hasIpv6 := false
for _, ip := range this.SourceCidr {
switch len(ip.Ip) {
case net.IPv4len:
ipv4Net.AddIP(ip.Ip, byte(ip.Prefix))
case net.IPv6len:
hasIpv6 = true
matcher, err := NewCIDRMatcher(ip.Ip, ip.Prefix, true)
if err != nil {
return nil, err
}
ipv6Cond.Add(matcher)
default:
return nil, errors.New("Router: Invalid IP length.")
}
}
if !ipv4Net.IsEmpty() && hasIpv6 {
cond := NewAnyCondition()
cond.Add(NewIPv4Matcher(ipv4Net, true))
cond.Add(ipv6Cond)
conds.Add(cond)
} else if !ipv4Net.IsEmpty() {
conds.Add(NewIPv4Matcher(ipv4Net, true))
} else if hasIpv6 {
conds.Add(ipv6Cond)
}
}
if len(this.UserEmail) > 0 {
conds.Add(NewUserMatcher(this.UserEmail))
}
if conds.Len() == 0 {
return nil, errors.New("Router: This rule has no effective fields.")
}

View File

@@ -10,7 +10,7 @@ It is generated from these files:
It has these top-level messages:
Domain
IP
CIDR
RoutingRule
Config
*/
@@ -60,8 +60,11 @@ func (Domain_Type) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []
type Config_DomainStrategy int32
const (
Config_AsIs Config_DomainStrategy = 0
Config_UseIp Config_DomainStrategy = 1
// Use domain as is.
Config_AsIs Config_DomainStrategy = 0
// Always resolve IP for domains.
Config_UseIp Config_DomainStrategy = 1
// Resolve to IP if the domain doesn't match any rules.
Config_IpIfNonMatch Config_DomainStrategy = 2
)
@@ -94,27 +97,27 @@ func (m *Domain) String() string { return proto.CompactTextString(m)
func (*Domain) ProtoMessage() {}
func (*Domain) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// IP for routing decision.
type IP struct {
// IP for routing decision, in CIDR form.
type CIDR struct {
// IP address, should be either 4 or 16 bytes.
Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"`
// Number of right-most bits in IP matching that is allowed.
// Single IP address like 127.0.0.1 should use unmatching_bits = 0.
// CIDR 10.0.0.0/8 should use unmatching_bits = 32-8 = 24.
UnmatchingBits uint32 `protobuf:"varint,2,opt,name=unmatching_bits,json=unmatchingBits" json:"unmatching_bits,omitempty"`
// Number of leading ones in the network mask.
Prefix uint32 `protobuf:"varint,2,opt,name=prefix" json:"prefix,omitempty"`
}
func (m *IP) Reset() { *m = IP{} }
func (m *IP) String() string { return proto.CompactTextString(m) }
func (*IP) ProtoMessage() {}
func (*IP) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *CIDR) Reset() { *m = CIDR{} }
func (m *CIDR) String() string { return proto.CompactTextString(m) }
func (*CIDR) ProtoMessage() {}
func (*CIDR) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
type RoutingRule struct {
Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"`
Domain []*Domain `protobuf:"bytes,2,rep,name=domain" json:"domain,omitempty"`
Ip []*IP `protobuf:"bytes,3,rep,name=ip" json:"ip,omitempty"`
Cidr []*CIDR `protobuf:"bytes,3,rep,name=cidr" json:"cidr,omitempty"`
PortRange *v2ray_core_common_net.PortRange `protobuf:"bytes,4,opt,name=port_range,json=portRange" json:"port_range,omitempty"`
NetworkList *v2ray_core_common_net1.NetworkList `protobuf:"bytes,5,opt,name=network_list,json=networkList" json:"network_list,omitempty"`
SourceCidr []*CIDR `protobuf:"bytes,6,rep,name=source_cidr,json=sourceCidr" json:"source_cidr,omitempty"`
UserEmail []string `protobuf:"bytes,7,rep,name=user_email,json=userEmail" json:"user_email,omitempty"`
}
func (m *RoutingRule) Reset() { *m = RoutingRule{} }
@@ -129,9 +132,9 @@ func (m *RoutingRule) GetDomain() []*Domain {
return nil
}
func (m *RoutingRule) GetIp() []*IP {
func (m *RoutingRule) GetCidr() []*CIDR {
if m != nil {
return m.Ip
return m.Cidr
}
return nil
}
@@ -150,6 +153,13 @@ func (m *RoutingRule) GetNetworkList() *v2ray_core_common_net1.NetworkList {
return nil
}
func (m *RoutingRule) GetSourceCidr() []*CIDR {
if m != nil {
return m.SourceCidr
}
return nil
}
type Config struct {
DomainStrategy Config_DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,enum=v2ray.core.app.router.Config_DomainStrategy" json:"domain_strategy,omitempty"`
Rule []*RoutingRule `protobuf:"bytes,2,rep,name=rule" json:"rule,omitempty"`
@@ -169,7 +179,7 @@ func (m *Config) GetRule() []*RoutingRule {
func init() {
proto.RegisterType((*Domain)(nil), "v2ray.core.app.router.Domain")
proto.RegisterType((*IP)(nil), "v2ray.core.app.router.IP")
proto.RegisterType((*CIDR)(nil), "v2ray.core.app.router.CIDR")
proto.RegisterType((*RoutingRule)(nil), "v2ray.core.app.router.RoutingRule")
proto.RegisterType((*Config)(nil), "v2ray.core.app.router.Config")
proto.RegisterEnum("v2ray.core.app.router.Domain_Type", Domain_Type_name, Domain_Type_value)
@@ -179,34 +189,37 @@ func init() {
func init() { proto.RegisterFile("v2ray.com/core/app/router/config.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 462 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x92, 0x5f, 0x6b, 0xd5, 0x30,
0x18, 0xc6, 0x6d, 0x77, 0x4e, 0xf1, 0xbc, 0x3d, 0x76, 0x25, 0x28, 0x74, 0x43, 0xa1, 0x14, 0x71,
0x47, 0x90, 0x54, 0x8e, 0xa8, 0x57, 0x22, 0x1e, 0xf5, 0xa2, 0xa0, 0xa3, 0x44, 0x77, 0xe3, 0xcd,
0x21, 0xeb, 0xb2, 0x1a, 0x6c, 0x93, 0x90, 0xa6, 0xd3, 0xf3, 0x11, 0xfc, 0x76, 0x7e, 0x24, 0x69,
0xd2, 0xb1, 0x4d, 0x56, 0xef, 0xf2, 0x86, 0xdf, 0xf3, 0xfe, 0x7d, 0xe0, 0xc9, 0xc5, 0x5a, 0xd3,
0x1d, 0xae, 0x64, 0x9b, 0x57, 0x52, 0xb3, 0x9c, 0x2a, 0x95, 0x6b, 0xd9, 0x1b, 0xa6, 0xf3, 0x4a,
0x8a, 0x73, 0x5e, 0x63, 0xa5, 0xa5, 0x91, 0xe8, 0xc1, 0x25, 0xa7, 0x19, 0xa6, 0x4a, 0x61, 0xc7,
0x1c, 0x3e, 0xfe, 0x47, 0x5e, 0xc9, 0xb6, 0x95, 0x22, 0x17, 0xcc, 0xe4, 0x4a, 0x6a, 0xe3, 0xc4,
0x87, 0x47, 0xd3, 0x94, 0x60, 0xe6, 0xa7, 0xd4, 0x3f, 0x1c, 0x98, 0x19, 0x08, 0x3e, 0xc8, 0x96,
0x72, 0x81, 0x5e, 0xc1, 0xcc, 0xec, 0x14, 0x4b, 0xbc, 0xd4, 0x5b, 0x45, 0xeb, 0x0c, 0xdf, 0x5a,
0x1e, 0x3b, 0x18, 0x7f, 0xdd, 0x29, 0x46, 0x2c, 0x8f, 0xee, 0xc3, 0xfc, 0x82, 0x36, 0x3d, 0x4b,
0xfc, 0xd4, 0x5b, 0x2d, 0x88, 0x0b, 0xb2, 0x87, 0x30, 0x1b, 0x18, 0xb4, 0x80, 0x79, 0xd9, 0x50,
0x2e, 0xe2, 0x3b, 0xc3, 0x93, 0xb0, 0x9a, 0xfd, 0x8a, 0xbd, 0xec, 0x0d, 0xf8, 0x45, 0x89, 0x22,
0xf0, 0xb9, 0xb2, 0xf5, 0x96, 0xc4, 0xe7, 0x0a, 0x1d, 0xc1, 0x7e, 0x2f, 0x5a, 0x6a, 0xaa, 0xef,
0x5c, 0xd4, 0xdb, 0x53, 0x6e, 0x3a, 0x9b, 0xf3, 0x1e, 0x89, 0xae, 0xbe, 0x37, 0xdc, 0x74, 0xd9,
0x6f, 0x1f, 0x42, 0x22, 0x7b, 0xc3, 0x45, 0x4d, 0xfa, 0x86, 0xa1, 0x18, 0xf6, 0x0c, 0xad, 0x6d,
0xa6, 0x05, 0x19, 0x9e, 0xe8, 0x25, 0x04, 0x67, 0xb6, 0xd3, 0xc4, 0x4f, 0xf7, 0x56, 0xe1, 0xfa,
0xd1, 0x7f, 0xc7, 0x21, 0x23, 0x8c, 0x9e, 0xda, 0x8e, 0xf6, 0xac, 0xe4, 0x60, 0x42, 0x52, 0x94,
0xb6, 0xd9, 0xb7, 0x00, 0xc3, 0xbe, 0xb7, 0x9a, 0x8a, 0x9a, 0x25, 0xb3, 0xd4, 0x5b, 0x85, 0xeb,
0xf4, 0xba, 0xc4, 0xad, 0x1c, 0x0b, 0x66, 0x70, 0x29, 0xb5, 0x21, 0x03, 0x47, 0x16, 0xea, 0xf2,
0x89, 0x3e, 0xc2, 0x72, 0x3c, 0xc5, 0xb6, 0xe1, 0x9d, 0x49, 0xe6, 0x36, 0x45, 0x36, 0x91, 0xe2,
0xd8, 0xa1, 0x9f, 0x78, 0x67, 0x48, 0x28, 0xae, 0x82, 0xec, 0x8f, 0x07, 0xc1, 0x7b, 0xeb, 0x1b,
0x74, 0x02, 0xfb, 0x6e, 0x8e, 0x6d, 0x67, 0x34, 0x35, 0xac, 0xde, 0x8d, 0xc7, 0x7c, 0x36, 0x31,
0x8a, 0xd3, 0x8d, 0x4b, 0xf8, 0x32, 0x6a, 0x48, 0x74, 0x76, 0x23, 0x1e, 0x8c, 0xa1, 0xfb, 0x86,
0x8d, 0x9b, 0x9c, 0x32, 0xc6, 0xb5, 0x7b, 0x10, 0xcb, 0x67, 0xaf, 0x21, 0xba, 0x99, 0x19, 0xdd,
0x85, 0xd9, 0xbb, 0xae, 0xe8, 0x9c, 0x17, 0x4e, 0x3a, 0x56, 0xa8, 0xd8, 0x43, 0x31, 0x2c, 0x0b,
0x55, 0x9c, 0x1f, 0x4b, 0xf1, 0x79, 0xb8, 0x71, 0xec, 0x6f, 0x9e, 0xc3, 0x41, 0x25, 0xdb, 0xdb,
0xeb, 0x6c, 0x42, 0xd7, 0x74, 0x39, 0xb8, 0xf7, 0x5b, 0xe0, 0x3e, 0x4f, 0x03, 0x6b, 0xe6, 0x17,
0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x93, 0xed, 0x34, 0x36, 0x5c, 0x03, 0x00, 0x00,
// 501 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x93, 0xd1, 0x6e, 0xd3, 0x3e,
0x14, 0xc6, 0xff, 0x49, 0xd3, 0xfc, 0xc9, 0x49, 0x29, 0x91, 0x05, 0x28, 0x0c, 0x26, 0x45, 0x11,
0x82, 0x5e, 0xa0, 0x04, 0x15, 0x01, 0x37, 0x48, 0x88, 0x75, 0xbb, 0xa8, 0x04, 0x53, 0x65, 0xd8,
0x0d, 0x37, 0x95, 0x49, 0xdd, 0x60, 0x91, 0xd8, 0x96, 0xe3, 0x8c, 0xf5, 0x2d, 0x79, 0x10, 0x1e,
0x02, 0xd9, 0xce, 0xc4, 0x86, 0x56, 0xb8, 0xf3, 0xb1, 0x7e, 0xdf, 0x39, 0x5f, 0x8e, 0xbf, 0xc0,
0x93, 0xf3, 0xb9, 0x22, 0xbb, 0xa2, 0x12, 0x6d, 0x59, 0x09, 0x45, 0x4b, 0x22, 0x65, 0xa9, 0x44,
0xaf, 0xa9, 0x2a, 0x2b, 0xc1, 0xb7, 0xac, 0x2e, 0xa4, 0x12, 0x5a, 0xa0, 0x7b, 0x97, 0x9c, 0xa2,
0x05, 0x91, 0xb2, 0x70, 0xcc, 0xc1, 0xe3, 0x3f, 0xe4, 0x95, 0x68, 0x5b, 0xc1, 0x4b, 0x4e, 0x75,
0x29, 0x85, 0xd2, 0x4e, 0x7c, 0xf0, 0x74, 0x3f, 0xc5, 0xa9, 0xfe, 0x2e, 0xd4, 0x37, 0x07, 0xe6,
0x1a, 0xc2, 0x63, 0xd1, 0x12, 0xc6, 0xd1, 0x2b, 0x08, 0xf4, 0x4e, 0xd2, 0xd4, 0xcb, 0xbc, 0xd9,
0x74, 0x9e, 0x17, 0x37, 0x8e, 0x2f, 0x1c, 0x5c, 0x7c, 0xda, 0x49, 0x8a, 0x2d, 0x8f, 0xee, 0xc2,
0xf8, 0x9c, 0x34, 0x3d, 0x4d, 0xfd, 0xcc, 0x9b, 0x45, 0xd8, 0x15, 0xf9, 0x23, 0x08, 0x0c, 0x83,
0x22, 0x18, 0xaf, 0x1a, 0xc2, 0x78, 0xf2, 0x9f, 0x39, 0x62, 0x5a, 0xd3, 0x8b, 0xc4, 0xcb, 0x0b,
0x08, 0x16, 0xcb, 0x63, 0x8c, 0xa6, 0xe0, 0x33, 0x69, 0x27, 0x4e, 0xb0, 0xcf, 0x24, 0xba, 0x0f,
0xa1, 0x54, 0x74, 0xcb, 0x2e, 0x6c, 0xb3, 0xdb, 0x78, 0xa8, 0xf2, 0x9f, 0x3e, 0xc4, 0x58, 0xf4,
0x9a, 0xf1, 0x1a, 0xf7, 0x0d, 0x45, 0x09, 0x8c, 0x34, 0xa9, 0xad, 0x30, 0xc2, 0xe6, 0x88, 0x5e,
0x42, 0xb8, 0xb1, 0xd6, 0x52, 0x3f, 0x1b, 0xcd, 0xe2, 0xf9, 0xe1, 0x5f, 0xfd, 0xe3, 0x01, 0x46,
0x25, 0x04, 0x15, 0xdb, 0xa8, 0x74, 0x64, 0x45, 0x0f, 0xf7, 0x88, 0x8c, 0x57, 0x6c, 0x41, 0xf4,
0x16, 0xc0, 0xac, 0x79, 0xad, 0x08, 0xaf, 0x69, 0x1a, 0x64, 0xde, 0x2c, 0x9e, 0x67, 0x57, 0x65,
0x6e, 0xd3, 0x05, 0xa7, 0xba, 0x58, 0x09, 0xa5, 0xb1, 0xe1, 0x70, 0x24, 0x2f, 0x8f, 0xe8, 0x04,
0x26, 0xc3, 0x0b, 0xac, 0x1b, 0xd6, 0xe9, 0x74, 0x6c, 0x5b, 0xe4, 0x7b, 0x5a, 0x9c, 0x3a, 0xf4,
0x3d, 0xeb, 0x34, 0x8e, 0xf9, 0xef, 0x02, 0xbd, 0x81, 0xb8, 0x13, 0xbd, 0xaa, 0xe8, 0xda, 0xfa,
0x0f, 0xff, 0xed, 0x1f, 0x1c, 0xbf, 0x30, 0x5f, 0x71, 0x08, 0xd0, 0x77, 0x54, 0xad, 0x69, 0x4b,
0x58, 0x93, 0xfe, 0x9f, 0x8d, 0x66, 0x11, 0x8e, 0xcc, 0xcd, 0x89, 0xb9, 0xc8, 0x7f, 0x78, 0x10,
0x2e, 0x6c, 0x16, 0xd1, 0x19, 0xdc, 0x71, 0xab, 0x5a, 0x77, 0x5a, 0x11, 0x4d, 0xeb, 0xdd, 0x10,
0x90, 0x67, 0xfb, 0x66, 0xb9, 0x0c, 0xbb, 0x3d, 0x7f, 0x1c, 0x34, 0x78, 0xba, 0xb9, 0x56, 0x9b,
0xb0, 0xa9, 0xbe, 0xa1, 0xc3, 0x63, 0xed, 0x0b, 0xdb, 0x95, 0x27, 0xc7, 0x96, 0xcf, 0x5f, 0xc3,
0xf4, 0x7a, 0x67, 0x74, 0x0b, 0x82, 0x77, 0xdd, 0xb2, 0x73, 0xf9, 0x3a, 0xeb, 0xe8, 0x52, 0x26,
0x1e, 0x4a, 0x60, 0xb2, 0x94, 0xcb, 0xed, 0xa9, 0xe0, 0x1f, 0x88, 0xae, 0xbe, 0x26, 0xfe, 0xd1,
0x73, 0x78, 0x50, 0x89, 0xf6, 0xe6, 0x39, 0x47, 0xb1, 0x33, 0xbd, 0x32, 0x7f, 0xc4, 0xe7, 0xd0,
0x5d, 0x7e, 0x09, 0xed, 0x0f, 0xf2, 0xe2, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x89, 0xc1,
0x51, 0xb0, 0x03, 0x00, 0x00,
}

View File

@@ -25,29 +25,34 @@ message Domain {
string value = 2;
}
// IP for routing decision.
message IP {
// IP for routing decision, in CIDR form.
message CIDR {
// IP address, should be either 4 or 16 bytes.
bytes ip = 1;
// Number of right-most bits in IP matching that is allowed.
// Single IP address like 127.0.0.1 should use unmatching_bits = 0.
// CIDR 10.0.0.0/8 should use unmatching_bits = 32-8 = 24.
uint32 unmatching_bits = 2;
// Number of leading ones in the network mask.
uint32 prefix = 2;
}
message RoutingRule {
string tag = 1;
repeated Domain domain = 2;
repeated IP ip = 3;
repeated CIDR cidr = 3;
v2ray.core.common.net.PortRange port_range = 4;
v2ray.core.common.net.NetworkList network_list = 5;
repeated CIDR source_cidr = 6;
repeated string user_email = 7;
}
message Config {
enum DomainStrategy {
// Use domain as is.
AsIs = 0;
// Always resolve IP for domains.
UseIp = 1;
// Resolve to IP if the domain doesn't match any rules.
IpIfNonMatch = 2;
}
DomainStrategy domain_strategy = 1;

View File

@@ -8,6 +8,7 @@ import (
"v2ray.com/core/common/loader"
"v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/proxy"
)
const (
@@ -22,15 +23,15 @@ var (
type Router struct {
domainStrategy Config_DomainStrategy
rules []Rule
cache *RoutingTable
dnsServer dns.Server
// cache *RoutingTable
dnsServer dns.Server
}
func NewRouter(config *Config, space app.Space) *Router {
r := &Router{
domainStrategy: config.DomainStrategy,
cache: NewRoutingTable(),
rules: make([]Rule, len(config.Rule)),
//cache: NewRoutingTable(),
rules: make([]Rule, len(config.Rule)),
}
space.InitializeApplication(func() error {
@@ -74,12 +75,13 @@ func (this *Router) ResolveIP(dest v2net.Destination) []v2net.Destination {
return dests
}
func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, error) {
func (this *Router) takeDetourWithoutCache(session *proxy.SessionInfo) (string, error) {
for _, rule := range this.rules {
if rule.Apply(dest) {
if rule.Apply(session) {
return rule.Tag, nil
}
}
dest := session.Destination
if this.domainStrategy == Config_IpIfNonMatch && dest.Address.Family().IsDomain() {
log.Info("Router: Looking up IP for ", dest)
ipDests := this.ResolveIP(dest)
@@ -87,7 +89,11 @@ func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, erro
for _, ipDest := range ipDests {
log.Info("Router: Trying IP ", ipDest)
for _, rule := range this.rules {
if rule.Apply(ipDest) {
if rule.Apply(&proxy.SessionInfo{
Source: session.Source,
Destination: ipDest,
User: session.User,
}) {
return rule.Tag, nil
}
}
@@ -98,15 +104,15 @@ func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, erro
return "", ErrNoRuleApplicable
}
func (this *Router) TakeDetour(dest v2net.Destination) (string, error) {
destStr := dest.String()
found, tag, err := this.cache.Get(destStr)
if !found {
tag, err := this.takeDetourWithoutCache(dest)
this.cache.Set(destStr, tag, err)
return tag, err
}
func (this *Router) TakeDetour(session *proxy.SessionInfo) (string, error) {
//destStr := dest.String()
//found, tag, err := this.cache.Get(destStr)
//if !found {
tag, err := this.takeDetourWithoutCache(session)
//this.cache.Set(destStr, tag, err)
return tag, err
//}
//return tag, err
}
type RouterFactory struct{}

View File

@@ -10,6 +10,7 @@ import (
"v2ray.com/core/app/proxyman"
. "v2ray.com/core/app/router"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/proxy"
"v2ray.com/core/testing/assert"
)
@@ -35,7 +36,7 @@ func TestSimpleRouter(t *testing.T) {
space.BindApp(APP_ID, r)
assert.Error(space.Initialize()).IsNil()
tag, err := r.TakeDetour(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80))
tag, err := r.TakeDetour(&proxy.SessionInfo{Destination: v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80)})
assert.Error(err).IsNil()
assert.String(tag).Equals("test")
}

View File

@@ -200,11 +200,6 @@ func (b *Buffer) String() string {
return string(b.Value)
}
// NewSmallBuffer creates a Buffer with 1K bytes of arbitrary content.
func NewSmallBuffer() *Buffer {
return smallPool.Allocate()
}
// NewBuffer creates a Buffer with 8K bytes of arbitrary content.
func NewBuffer() *Buffer {
return mediumPool.Allocate()
@@ -216,10 +211,6 @@ func NewLargeBuffer() *Buffer {
}
func NewBufferWithSize(size int) *Buffer {
if size <= SmallBufferSize {
return NewSmallBuffer()
}
if size <= BufferSize {
return NewBuffer()
}

View File

@@ -52,8 +52,6 @@ func (p *BufferPool) Free(buffer *Buffer) {
}
const (
SmallBufferSize = 1600 - defaultOffset
mediumBufferByteSize = 8 * 1024
BufferSize = mediumBufferByteSize - defaultOffset
@@ -64,7 +62,6 @@ const (
)
var (
smallPool = NewBufferPool(1600, 256)
mediumPool *BufferPool
largePool *BufferPool
)

View File

@@ -1,12 +0,0 @@
package collect
type StringList []string
func NewStringList(raw []string) *StringList {
list := StringList(raw)
return &list
}
func (this StringList) Len() int {
return len(this)
}

View File

@@ -1,25 +0,0 @@
// +build json
package collect
import (
"encoding/json"
"errors"
"strings"
)
func (this *StringList) UnmarshalJSON(data []byte) error {
var strarray []string
if err := json.Unmarshal(data, &strarray); err == nil {
*this = *NewStringList(strarray)
return nil
}
var rawstr string
if err := json.Unmarshal(data, &rawstr); err == nil {
strlist := strings.Split(rawstr, ",")
*this = *NewStringList(strlist)
return nil
}
return errors.New("Unknown format of a string list: " + string(data))
}

View File

@@ -1,30 +0,0 @@
// +build json
package collect_test
import (
"encoding/json"
"testing"
. "v2ray.com/core/common/collect"
"v2ray.com/core/testing/assert"
)
func TestStringListUnmarshalError(t *testing.T) {
assert := assert.On(t)
rawJson := `1234`
list := new(StringList)
err := json.Unmarshal([]byte(rawJson), list)
assert.Error(err).IsNotNil()
}
func TestStringListLen(t *testing.T) {
assert := assert.On(t)
rawJson := `"a, b, c, d"`
list := new(StringList)
err := json.Unmarshal([]byte(rawJson), list)
assert.Error(err).IsNil()
assert.Int(list.Len()).Equals(4)
}

View File

@@ -1,36 +0,0 @@
package loader
import (
"errors"
"v2ray.com/core/common"
)
var (
ErrUnknownConfigID = errors.New("Unknown config ID.")
)
type ConfigCreator func() interface{}
type ConfigCreatorCache map[string]ConfigCreator
func (this ConfigCreatorCache) RegisterCreator(id string, creator ConfigCreator) error {
if _, found := this[id]; found {
return common.ErrDuplicatedName
}
this[id] = creator
return nil
}
func (this ConfigCreatorCache) CreateConfig(id string) (interface{}, error) {
creator, found := this[id]
if !found {
return nil, ErrUnknownConfigID
}
return creator(), nil
}
type ConfigLoader interface {
Load([]byte) (interface{}, string, error)
LoadWithID([]byte, string) (interface{}, error)
}

View File

@@ -28,8 +28,11 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Serialized proto message along with its type name.
type TypedSettings struct {
Type string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"`
// The name of the message type, retrieved from protobuf API.
Type string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"`
// Serialized proto message.
Settings []byte `protobuf:"bytes,2,opt,name=settings,proto3" json:"settings,omitempty"`
}

View File

@@ -5,7 +5,10 @@ option go_package = "loader";
option java_package = "com.v2ray.core.common.loader";
option java_outer_classname = "TypeProto";
// Serialized proto message along with its type name.
message TypedSettings {
// The name of the message type, retrieved from protobuf API.
string type = 1;
// Serialized proto message.
bytes settings = 2;
}

View File

@@ -7,7 +7,10 @@ option java_outer_classname = "AddressProto";
message IPOrDomain {
oneof address {
// IP address. Must by either 4 or 16 bytes.
bytes ip = 1;
// Domain address.
string domain = 2;
}
}

View File

@@ -13,12 +13,8 @@ type IPNet struct {
}
func NewIPNet() *IPNet {
return NewIPNetInitialValue(make(map[uint32]byte, 1024))
}
func NewIPNetInitialValue(data map[uint32]byte) *IPNet {
return &IPNet{
cache: data,
cache: make(map[uint32]byte, 1024),
}
}
@@ -45,11 +41,15 @@ func (this *IPNet) Add(ipNet *net.IPNet) {
// For now, we don't support IPv6
return
}
value := ipToUint32(ipv4)
mask := ipMaskToByte(ipNet.Mask)
existing, found := this.cache[value]
this.AddIP(ipv4, mask)
}
func (this *IPNet) AddIP(ip []byte, mask byte) {
k := ipToUint32(ip)
existing, found := this.cache[k]
if !found || existing > mask {
this.cache[value] = mask
this.cache[k] = mask
}
}
@@ -61,7 +61,7 @@ func (this *IPNet) Contains(ip net.IP) bool {
originalValue := ipToUint32(ipv4)
if entry, found := this.cache[originalValue]; found {
if entry == 0 {
if entry == 32 {
return true
}
}
@@ -80,12 +80,8 @@ func (this *IPNet) Contains(ip net.IP) bool {
return false
}
func (this *IPNet) Serialize() []uint32 {
content := make([]uint32, 0, 2*len(this.cache))
for key, value := range this.cache {
content = append(content, uint32(key), uint32(value))
}
return content
func (this *IPNet) IsEmpty() bool {
return len(this.cache) == 0
}
func init() {

View File

@@ -2,8 +2,6 @@ package net
import (
"strings"
"v2ray.com/core/common/collect"
)
func ParseNetwork(nwStr string) Network {
@@ -56,17 +54,6 @@ func (this Network) UrlPrefix() string {
}
}
// NewNetworkList construsts a NetWorklist from the given StringListeralList.
func NewNetworkList(networks collect.StringList) *NetworkList {
list := &NetworkList{
Network: make([]Network, networks.Len()),
}
for idx, network := range networks {
list.Network[idx] = ParseNetwork(network)
}
return list
}
// HashNetwork returns true if the given network is in this NetworkList.
func (this NetworkList) HasNetwork(network Network) bool {
for _, value := range this.Network {

View File

@@ -16,8 +16,10 @@ var _ = math.Inf
type Network int32
const (
Network_Unknown Network = 0
Network_RawTCP Network = 1
Network_Unknown Network = 0
// Native TCP provided by system.
Network_RawTCP Network = 1
// V2Ray specific TCP.
Network_TCP Network = 2
Network_UDP Network = 3
Network_KCP Network = 4

View File

@@ -7,10 +7,17 @@ option java_outer_classname = "NetworkProto";
enum Network {
Unknown = 0;
// Native TCP provided by system.
RawTCP = 1;
// V2Ray specific TCP.
TCP = 2;
UDP = 3;
KCP = 4;
WebSocket = 5;
}

View File

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

View File

@@ -1,48 +0,0 @@
// +build json
package net_test
import (
"encoding/json"
"testing"
. "v2ray.com/core/common/net"
"v2ray.com/core/testing/assert"
)
func TestStringNetwork(t *testing.T) {
assert := assert.On(t)
var network Network
err := json.Unmarshal([]byte(`"tcp"`), &network)
assert.Error(err).IsNil()
assert.Bool(network == Network_TCP).IsTrue()
}
func TestArrayNetworkList(t *testing.T) {
assert := assert.On(t)
var list NetworkList
err := json.Unmarshal([]byte("[\"Tcp\"]"), &list)
assert.Error(err).IsNil()
assert.Bool(list.HasNetwork(ParseNetwork("tcp"))).IsTrue()
assert.Bool(list.HasNetwork(ParseNetwork("udp"))).IsFalse()
}
func TestStringNetworkList(t *testing.T) {
assert := assert.On(t)
var list NetworkList
err := json.Unmarshal([]byte("\"TCP, ip\""), &list)
assert.Error(err).IsNil()
assert.Bool(list.HasNetwork(ParseNetwork("tcp"))).IsTrue()
assert.Bool(list.HasNetwork(ParseNetwork("udp"))).IsFalse()
}
func TestInvalidNetworkJson(t *testing.T) {
assert := assert.On(t)
var list NetworkList
err := json.Unmarshal([]byte("0"), &list)
assert.Error(err).IsNotNil()
}

View File

@@ -13,9 +13,9 @@ func TestServerList(t *testing.T) {
assert := assert.On(t)
list := NewServerList()
list.AddServer(NewServerSpec(nil, v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(1)), AlwaysValid()))
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(1)), AlwaysValid()))
assert.Uint32(list.Size()).Equals(1)
list.AddServer(NewServerSpec(nil, v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
assert.Uint32(list.Size()).Equals(2)
server := list.GetServer(1)
@@ -32,9 +32,9 @@ func TestServerPicker(t *testing.T) {
assert := assert.On(t)
list := NewServerList()
list.AddServer(NewServerSpec(nil, v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(1)), AlwaysValid()))
list.AddServer(NewServerSpec(nil, v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
list.AddServer(NewServerSpec(nil, v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(3)), BeforeTime(time.Now().Add(time.Second))))
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(1)), AlwaysValid()))
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(3)), BeforeTime(time.Now().Add(time.Second))))
picker := NewRoundRobinServerPicker(list)
server := picker.PickServer()

View File

@@ -45,24 +45,22 @@ func (this *TimeoutValidStrategy) Invalidate() {
type ServerSpec struct {
sync.RWMutex
dest v2net.Destination
users []*User
valid ValidationStrategy
newAccount NewAccountFactory
dest v2net.Destination
users []*User
valid ValidationStrategy
}
func NewServerSpec(newAccount NewAccountFactory, dest v2net.Destination, valid ValidationStrategy, users ...*User) *ServerSpec {
func NewServerSpec(dest v2net.Destination, valid ValidationStrategy, users ...*User) *ServerSpec {
return &ServerSpec{
dest: dest,
users: users,
valid: valid,
newAccount: newAccount,
dest: dest,
users: users,
valid: valid,
}
}
func NewServerSpecFromPB(newAccount NewAccountFactory, spec ServerEndpoint) *ServerSpec {
func NewServerSpecFromPB(spec ServerEndpoint) *ServerSpec {
dest := v2net.TCPDestination(spec.Address.AsAddress(), v2net.Port(spec.Port))
return NewServerSpec(newAccount, dest, AlwaysValid(), spec.User...)
return NewServerSpec(dest, AlwaysValid(), spec.User...)
}
func (this *ServerSpec) Destination() v2net.Destination {

View File

@@ -15,8 +15,9 @@ var _ = fmt.Errorf
var _ = math.Inf
type User struct {
Level uint32 `protobuf:"varint,1,opt,name=level" json:"level,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email" json:"email,omitempty"`
Level uint32 `protobuf:"varint,1,opt,name=level" json:"level,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email" json:"email,omitempty"`
// Protocol specific account information.
Account *v2ray_core_common_loader.TypedSettings `protobuf:"bytes,3,opt,name=account" json:"account,omitempty"`
}

View File

@@ -10,5 +10,7 @@ import "v2ray.com/core/common/loader/type.proto";
message User {
uint32 level = 1;
string email = 2;
// Protocol specific account information.
v2ray.core.common.loader.TypedSettings account = 3;
}

View File

@@ -46,3 +46,10 @@ func (this *OutboundConnectionConfig) GetTypedSettings() (interface{}, error) {
}
return this.GetSettings().GetInstance()
}
func (this *OutboundConnectionConfig) GetSendThroughValue() v2net.Address {
if this.GetSendThrough() == nil {
return v2net.AnyIP
}
return this.SendThrough.AsAddress()
}

View File

@@ -39,6 +39,7 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Configuration serialization format.
type ConfigFormat int32
const (
@@ -108,8 +109,10 @@ func (*AllocationStrategyRefresh) Descriptor() ([]byte, []int) { return fileDesc
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"`
}
@@ -134,9 +137,13 @@ func (m *AllocationStrategy) GetRefresh() *AllocationStrategyRefresh {
// Config for an inbound connection handler.
type InboundConnectionConfig struct {
Settings *v2ray_core_common_loader.TypedSettings `protobuf:"bytes,1,opt,name=settings" json:"settings,omitempty"`
PortRange *v2ray_core_common_net.PortRange `protobuf:"bytes,2,opt,name=port_range,json=portRange" json:"port_range,omitempty"`
ListenOn *v2ray_core_common_net1.IPOrDomain `protobuf:"bytes,3,opt,name=listen_on,json=listenOn" json:"listen_on,omitempty"`
// Protocol specific settings. Must be one of the supported protocols.
Settings *v2ray_core_common_loader.TypedSettings `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"`
@@ -183,8 +190,10 @@ func (m *InboundConnectionConfig) GetStreamSettings() *v2ray_core_transport_inte
return nil
}
// Config for an outbound connection handler.
type OutboundConnectionConfig struct {
Settings *v2ray_core_common_loader.TypedSettings `protobuf:"bytes,1,opt,name=settings" json:"settings,omitempty"`
Settings *v2ray_core_common_loader.TypedSettings `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"`
Tag string `protobuf:"bytes,4,opt,name=tag" json:"tag,omitempty"`
@@ -217,9 +226,12 @@ func (m *OutboundConnectionConfig) GetStreamSettings() *v2ray_core_transport_int
}
type Config struct {
Inbound []*InboundConnectionConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"`
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"`
// Inbound handler configurations. Must have at least one item.
Inbound []*InboundConnectionConfig `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"`
// App configuration. Must be one in the app directory.
App []*v2ray_core_common_loader.TypedSettings `protobuf:"bytes,4,rep,name=app" json:"app,omitempty"`
Transport *v2ray_core_transport.Config `protobuf:"bytes,5,opt,name=transport" json:"transport,omitempty"`
}

View File

@@ -12,6 +12,7 @@ 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.
enum ConfigFormat {
Protobuf = 0;
JSON = 1;
@@ -40,34 +41,54 @@ message AllocationStrategy {
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.loader.TypedSettings 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.loader.TypedSettings 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;
string tag = 4;
}
message Config {
// Inbound handler configurations. Must have at least one item.
repeated InboundConnectionConfig inbound = 1;
// Outbound handler configurations. Must have at least one item. The first item is used as default for routing.
repeated OutboundConnectionConfig outbound = 2;
v2ray.core.common.log.Config log = 3;
// App configuration. Must be one in the app directory.
repeated v2ray.core.common.loader.TypedSettings app = 4;
v2ray.core.transport.Config transport = 5;
}

View File

@@ -8,7 +8,7 @@ import (
)
var (
version = "2.4"
version = "2.6"
build = "Custom"
codename = "One for all"
intro = "An unified platform for anti-censorship."

View File

@@ -109,7 +109,7 @@ func (this *InboundDetourHandlerDynamic) refresh() error {
port := this.pickUnusedPort()
ichConfig, _ := config.GetTypedSettings()
ich, err := proxyregistry.CreateInboundHandler(config.Settings.Type, this.space, ichConfig, &proxy.InboundHandlerMeta{
Address: config.ListenOn.AsAddress(), Port: port, Tag: config.Tag, StreamSettings: config.StreamSettings})
Address: config.GetListenOnValue(), Port: port, Tag: config.Tag, StreamSettings: config.StreamSettings})
if err != nil {
delete(this.portsInUse, port)
return err

View File

@@ -80,7 +80,9 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *
destination = this.ResolveIP(destination)
}
err := retry.Timed(5, 100).On(func() error {
rawConn, err := internet.Dial(this.meta.Address, destination, this.meta.StreamSettings)
rawConn, err := internet.Dial(this.meta.Address, destination, internet.DialerOptions{
Stream: this.meta.StreamSettings,
})
if err != nil {
return err
}

168
proxy/shadowsocks/client.go Normal file
View File

@@ -0,0 +1,168 @@
package shadowsocks
import (
"errors"
"sync"
"v2ray.com/core/app"
"v2ray.com/core/common/alloc"
v2io "v2ray.com/core/common/io"
"v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/retry"
"v2ray.com/core/proxy"
"v2ray.com/core/transport/internet"
"v2ray.com/core/transport/ray"
)
type Client struct {
serverPicker protocol.ServerPicker
meta *proxy.OutboundHandlerMeta
}
func NewClient(config *ClientConfig, space app.Space, meta *proxy.OutboundHandlerMeta) (*Client, error) {
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 (this *Client) Dispatch(destination v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error {
defer payload.Release()
defer ray.OutboundInput().Release()
defer ray.OutboundOutput().Close()
network := destination.Network
var server *protocol.ServerSpec
var conn internet.Connection
err := retry.Timed(5, 100).On(func() error {
server = this.serverPicker.PickServer()
dest := server.Destination()
dest.Network = network
rawConn, err := internet.Dial(this.meta.Address, dest, internet.DialerOptions{
Stream: this.meta.StreamSettings,
})
if err != nil {
return err
}
conn = rawConn
return nil
})
if err != nil {
return errors.New("Shadowsocks|Client: Failed to find an available destination:" + err.Error())
}
log.Info("Shadowsocks|Client: Tunneling request to ", destination, " via ", server.Destination())
conn.SetReusable(false)
request := &protocol.RequestHeader{
Version: Version,
Address: destination.Address,
Port: destination.Port,
}
if destination.Network == v2net.Network_TCP {
request.Command = protocol.RequestCommandTCP
} else {
request.Command = protocol.RequestCommandUDP
}
user := server.PickUser()
rawAccount, err := user.GetTypedAccount()
if err != nil {
return errors.New("Shadowsocks|Client: Failed to get a valid user account: " + err.Error())
}
account := rawAccount.(*ShadowsocksAccount)
request.User = user
if account.OneTimeAuth == Account_Auto || account.OneTimeAuth == Account_Enabled {
request.Option |= RequestOptionOneTimeAuth
}
if request.Command == protocol.RequestCommandTCP {
bufferedWriter := v2io.NewBufferedWriter(conn)
defer bufferedWriter.Release()
bodyWriter, err := WriteTCPRequest(request, bufferedWriter)
defer bodyWriter.Release()
if err != nil {
return errors.New("Shadowsock|Client: Failed to write request: " + err.Error())
}
if err := bodyWriter.Write(payload); err != nil {
return errors.New("Shadowsocks|Client: Failed to write payload: " + err.Error())
}
var responseMutex sync.Mutex
responseMutex.Lock()
go func() {
defer responseMutex.Unlock()
responseReader, err := ReadTCPResponse(user, conn)
if err != nil {
log.Warning("Shadowsocks|Client: Failed to read response: " + err.Error())
return
}
v2io.Pipe(responseReader, ray.OutboundOutput())
}()
bufferedWriter.SetCached(false)
v2io.Pipe(ray.OutboundInput(), bodyWriter)
responseMutex.Lock()
}
if request.Command == protocol.RequestCommandUDP {
timedReader := v2net.NewTimeOutReader(16, conn)
var responseMutex sync.Mutex
responseMutex.Lock()
go func() {
defer responseMutex.Unlock()
reader := &UDPReader{
Reader: timedReader,
User: user,
}
v2io.Pipe(reader, ray.OutboundOutput())
}()
writer := &UDPWriter{
Writer: conn,
Request: request,
}
if err := writer.Write(payload); err != nil {
return errors.New("Shadowsocks|Client: Failed to write payload: " + err.Error())
}
v2io.Pipe(ray.OutboundInput(), writer)
responseMutex.Lock()
}
return nil
}
type ClientFactory struct{}
func (this *ClientFactory) StreamCapability() v2net.NetworkList {
return v2net.NetworkList{
Network: []v2net.Network{v2net.Network_TCP, v2net.Network_RawTCP},
}
}
func (this *ClientFactory) Create(space app.Space, rawConfig interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
return NewClient(rawConfig.(*ClientConfig), space, meta)
}

View File

@@ -1,6 +1,7 @@
package shadowsocks
import (
"bytes"
"crypto/cipher"
"crypto/md5"
"errors"
@@ -9,6 +10,19 @@ import (
"v2ray.com/core/common/protocol"
)
type ShadowsocksAccount struct {
Cipher Cipher
Key []byte
OneTimeAuth Account_OneTimeAuth
}
func (this *ShadowsocksAccount) Equals(another protocol.Account) bool {
if account, ok := another.(*ShadowsocksAccount); ok {
return bytes.Equal(this.Key, account.Key)
}
return false
}
func (this *Account) GetCipher() (Cipher, error) {
switch this.CipherType {
case CipherType_AES_128_CFB:
@@ -24,15 +38,16 @@ func (this *Account) GetCipher() (Cipher, error) {
}
}
func (this *Account) Equals(another protocol.Account) bool {
if account, ok := another.(*Account); ok {
return account.Password == this.Password
}
return false
}
func (this *Account) AsAccount() (protocol.Account, error) {
return this, nil
cipher, err := this.GetCipher()
if err != nil {
return nil, err
}
return &ShadowsocksAccount{
Cipher: cipher,
Key: this.GetCipherKey(),
OneTimeAuth: this.Ota,
}, nil
}
func (this *Account) GetCipherKey() []byte {

View File

@@ -62,9 +62,34 @@ func (x CipherType) String() string {
}
func (CipherType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type Account_OneTimeAuth int32
const (
Account_Auto Account_OneTimeAuth = 0
Account_Disabled Account_OneTimeAuth = 1
Account_Enabled Account_OneTimeAuth = 2
)
var Account_OneTimeAuth_name = map[int32]string{
0: "Auto",
1: "Disabled",
2: "Enabled",
}
var Account_OneTimeAuth_value = map[string]int32{
"Auto": 0,
"Disabled": 1,
"Enabled": 2,
}
func (x Account_OneTimeAuth) String() string {
return proto.EnumName(Account_OneTimeAuth_name, int32(x))
}
func (Account_OneTimeAuth) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} }
type Account struct {
Password string `protobuf:"bytes,1,opt,name=password" json:"password,omitempty"`
CipherType CipherType `protobuf:"varint,2,opt,name=cipher_type,json=cipherType,enum=v2ray.core.proxy.shadowsocks.CipherType" json:"cipher_type,omitempty"`
Password string `protobuf:"bytes,1,opt,name=password" json:"password,omitempty"`
CipherType CipherType `protobuf:"varint,2,opt,name=cipher_type,json=cipherType,enum=v2ray.core.proxy.shadowsocks.CipherType" json:"cipher_type,omitempty"`
Ota Account_OneTimeAuth `protobuf:"varint,3,opt,name=ota,enum=v2ray.core.proxy.shadowsocks.Account_OneTimeAuth" json:"ota,omitempty"`
}
func (m *Account) Reset() { *m = Account{} }
@@ -110,34 +135,38 @@ func init() {
proto.RegisterType((*ServerConfig)(nil), "v2ray.core.proxy.shadowsocks.ServerConfig")
proto.RegisterType((*ClientConfig)(nil), "v2ray.core.proxy.shadowsocks.ClientConfig")
proto.RegisterEnum("v2ray.core.proxy.shadowsocks.CipherType", CipherType_name, CipherType_value)
proto.RegisterEnum("v2ray.core.proxy.shadowsocks.Account_OneTimeAuth", Account_OneTimeAuth_name, Account_OneTimeAuth_value)
}
func init() { proto.RegisterFile("v2ray.com/core/proxy/shadowsocks/config.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 371 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x50, 0xdd, 0xab, 0xd3, 0x30,
0x14, 0xb7, 0xf7, 0x5e, 0xee, 0x9d, 0x27, 0x53, 0x6b, 0x9e, 0xc6, 0x10, 0x2c, 0x7b, 0xaa, 0x17,
0x4c, 0x67, 0xfd, 0xc0, 0x07, 0x5f, 0xda, 0xd2, 0xb1, 0x21, 0x4c, 0xe9, 0x36, 0x04, 0x11, 0x4a,
0x97, 0x46, 0x57, 0x5c, 0x9b, 0x90, 0xb4, 0x9b, 0xfd, 0xef, 0x65, 0xc9, 0x3a, 0x87, 0x0f, 0xbb,
0x6f, 0x39, 0x27, 0xbf, 0xcf, 0x03, 0xaf, 0x77, 0xbe, 0xcc, 0x5a, 0x42, 0x79, 0xe9, 0x51, 0x2e,
0x99, 0x27, 0x24, 0xff, 0xd3, 0x7a, 0x6a, 0x93, 0xe5, 0x7c, 0xaf, 0x38, 0xfd, 0xad, 0x3c, 0xca,
0xab, 0x9f, 0xc5, 0x2f, 0x22, 0x24, 0xaf, 0x39, 0x7e, 0xd1, 0xc1, 0x25, 0x23, 0x1a, 0x4a, 0xce,
0xa0, 0xc3, 0x57, 0xff, 0x89, 0x51, 0x5e, 0x96, 0xbc, 0xf2, 0x34, 0x95, 0xf2, 0xad, 0xd7, 0x28,
0x26, 0x8d, 0xd0, 0x70, 0xfc, 0x00, 0x54, 0x31, 0xb9, 0x63, 0x32, 0x55, 0x82, 0x51, 0xc3, 0x18,
0x09, 0xb8, 0x0b, 0x28, 0xe5, 0x4d, 0x55, 0xe3, 0x21, 0xf4, 0x44, 0xa6, 0xd4, 0x9e, 0xcb, 0x7c,
0x60, 0x39, 0x96, 0xfb, 0x38, 0x39, 0xcd, 0x78, 0x06, 0x88, 0x16, 0x62, 0xc3, 0x64, 0x5a, 0xb7,
0x82, 0x0d, 0xae, 0x1c, 0xcb, 0x7d, 0xea, 0xbb, 0xe4, 0x52, 0x6e, 0x12, 0x69, 0xc2, 0xb2, 0x15,
0x2c, 0x01, 0x7a, 0x7a, 0x8f, 0x18, 0xf4, 0x17, 0x3a, 0x46, 0xa4, 0x4f, 0x80, 0x5f, 0x02, 0x6a,
0x72, 0x91, 0xb2, 0x2a, 0x5b, 0x6f, 0x99, 0x71, 0xee, 0x25, 0xd0, 0xe4, 0x22, 0x36, 0x1b, 0xfc,
0x0e, 0x6e, 0x0e, 0x15, 0xb5, 0x29, 0xf2, 0x9d, 0x73, 0x53, 0xd3, 0x8f, 0x74, 0xfd, 0xc8, 0x4a,
0x31, 0x99, 0x68, 0xf4, 0x28, 0x81, 0x7e, 0xb4, 0x2d, 0x58, 0x55, 0x1f, 0x6d, 0x42, 0xb8, 0x35,
0xed, 0x07, 0x96, 0x73, 0xed, 0x22, 0xff, 0xfe, 0x92, 0x8e, 0x09, 0x18, 0x57, 0xb9, 0xe0, 0x45,
0x55, 0x27, 0x47, 0xe6, 0xfd, 0x0f, 0x80, 0x7f, 0xa5, 0x30, 0x82, 0xbb, 0xd5, 0xfc, 0xf3, 0xfc,
0xcb, 0xb7, 0xb9, 0xfd, 0x08, 0x3f, 0x03, 0x14, 0xc4, 0x8b, 0xf4, 0x8d, 0xff, 0x31, 0x8d, 0x26,
0xa1, 0x6d, 0x75, 0x0b, 0xff, 0xfd, 0x07, 0xbd, 0xb8, 0xc2, 0x7d, 0xe8, 0x45, 0xd3, 0x20, 0x9a,
0x06, 0xfe, 0xd8, 0xbe, 0xc6, 0xcf, 0xe1, 0x49, 0x37, 0xa5, 0xb3, 0x78, 0xb2, 0xb4, 0x6f, 0xc2,
0x4f, 0xe0, 0x50, 0x5e, 0x5e, 0xbc, 0x69, 0x88, 0x4c, 0x9b, 0xaf, 0x87, 0xa0, 0xdf, 0xd1, 0xd9,
0xcf, 0xfa, 0x56, 0x87, 0x7f, 0xfb, 0x37, 0x00, 0x00, 0xff, 0xff, 0xff, 0xed, 0x11, 0xa8, 0x7b,
0x02, 0x00, 0x00,
// 431 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x91, 0x4f, 0x6f, 0xd3, 0x40,
0x10, 0xc5, 0xeb, 0x24, 0x6a, 0xc3, 0x6c, 0x00, 0xb3, 0xa7, 0x28, 0x42, 0xc2, 0xca, 0x29, 0x54,
0x62, 0xdd, 0x9a, 0x3f, 0xe2, 0xc0, 0xc5, 0x31, 0xa9, 0x5a, 0x21, 0xa5, 0xc8, 0x4d, 0x85, 0x84,
0x90, 0x2c, 0x77, 0x3d, 0x10, 0x8b, 0xd8, 0xb3, 0xda, 0xb5, 0x5b, 0xf2, 0x91, 0xf9, 0x16, 0xc8,
0xeb, 0x24, 0x44, 0x1c, 0xc2, 0xcd, 0x33, 0x7e, 0xef, 0xf9, 0xcd, 0xcf, 0xf0, 0xea, 0x3e, 0xd0,
0xe9, 0x5a, 0x48, 0x2a, 0x7c, 0x49, 0x1a, 0x7d, 0xa5, 0xe9, 0xd7, 0xda, 0x37, 0xcb, 0x34, 0xa3,
0x07, 0x43, 0xf2, 0xa7, 0xf1, 0x25, 0x95, 0xdf, 0xf3, 0x1f, 0x42, 0x69, 0xaa, 0x88, 0x3f, 0xdf,
0xca, 0x35, 0x0a, 0x2b, 0x15, 0x7b, 0xd2, 0xd1, 0xcb, 0x7f, 0xc2, 0x24, 0x15, 0x05, 0x95, 0xbe,
0xb5, 0x4a, 0x5a, 0xf9, 0xb5, 0x41, 0xdd, 0x06, 0x8d, 0xce, 0xfe, 0x23, 0x35, 0xa8, 0xef, 0x51,
0x27, 0x46, 0xa1, 0x6c, 0x1d, 0xe3, 0xdf, 0x0e, 0x9c, 0x84, 0x52, 0x52, 0x5d, 0x56, 0x7c, 0x04,
0x7d, 0x95, 0x1a, 0xf3, 0x40, 0x3a, 0x1b, 0x3a, 0x9e, 0x33, 0x79, 0x14, 0xef, 0x66, 0x7e, 0x05,
0x4c, 0xe6, 0x6a, 0x89, 0x3a, 0xa9, 0xd6, 0x0a, 0x87, 0x1d, 0xcf, 0x99, 0x3c, 0x09, 0x26, 0xe2,
0x50, 0x71, 0x11, 0x59, 0xc3, 0x62, 0xad, 0x30, 0x06, 0xb9, 0x7b, 0xe6, 0x11, 0x74, 0xa9, 0x4a,
0x87, 0x5d, 0x1b, 0x71, 0x7e, 0x38, 0x62, 0x53, 0x4d, 0x5c, 0x97, 0xb8, 0xc8, 0x0b, 0x0c, 0xeb,
0x6a, 0x19, 0x37, 0xee, 0x71, 0x00, 0x6c, 0x6f, 0xc7, 0xfb, 0xd0, 0x0b, 0xeb, 0x8a, 0xdc, 0x23,
0x3e, 0x80, 0xfe, 0xc7, 0xdc, 0xa4, 0x77, 0x2b, 0xcc, 0x5c, 0x87, 0x33, 0x38, 0x99, 0x95, 0xed,
0xd0, 0x19, 0x23, 0x0c, 0x6e, 0x2c, 0x80, 0xc8, 0xc2, 0xe7, 0x2f, 0x80, 0xd5, 0x99, 0x4a, 0xb0,
0x15, 0xd8, 0x93, 0xfb, 0x31, 0xd4, 0x99, 0xda, 0x58, 0xf8, 0x1b, 0xe8, 0x35, 0x70, 0xed, 0xb5,
0x2c, 0xf0, 0xf6, 0xab, 0xb6, 0x64, 0xc5, 0x96, 0xac, 0xb8, 0x35, 0xa8, 0x63, 0xab, 0x1e, 0xc7,
0x30, 0x88, 0x56, 0x39, 0x96, 0xd5, 0xe6, 0x33, 0x53, 0x38, 0x6e, 0xb9, 0x0f, 0x1d, 0xaf, 0x3b,
0x61, 0xc1, 0xe9, 0xa1, 0x9c, 0xb6, 0xe0, 0xac, 0xcc, 0x14, 0xe5, 0x65, 0x15, 0x6f, 0x9c, 0xa7,
0xdf, 0x00, 0xfe, 0xd2, 0x6c, 0xae, 0xba, 0x9d, 0x7f, 0x9a, 0x5f, 0x7f, 0x99, 0xbb, 0x47, 0xfc,
0x29, 0xb0, 0x70, 0x76, 0x93, 0x9c, 0x07, 0xef, 0x93, 0xe8, 0x62, 0xea, 0x3a, 0xdb, 0x45, 0xf0,
0xf6, 0x9d, 0x5d, 0x74, 0x1a, 0x24, 0xd1, 0x65, 0x18, 0x5d, 0x86, 0xc1, 0x99, 0xdb, 0xe5, 0xcf,
0xe0, 0xf1, 0x76, 0x4a, 0xae, 0x66, 0x17, 0x0b, 0xb7, 0x37, 0xfd, 0x00, 0x9e, 0xa4, 0xe2, 0xe0,
0x9f, 0x98, 0xb2, 0xf6, 0x9a, 0xcf, 0x4d, 0xd1, 0xaf, 0x6c, 0xef, 0xcd, 0xdd, 0xb1, 0x2d, 0xff,
0xfa, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x59, 0xf6, 0xcd, 0xed, 0xf5, 0x02, 0x00, 0x00,
}

View File

@@ -9,8 +9,14 @@ import "v2ray.com/core/common/protocol/user.proto";
import "v2ray.com/core/common/protocol/server_spec.proto";
message Account {
enum OneTimeAuth {
Auto = 0;
Disabled = 1;
Enabled = 2;
}
string password = 1;
CipherType cipher_type = 2;
OneTimeAuth ota = 3;
}
enum CipherType {

12
proxy/shadowsocks/init.go Normal file
View File

@@ -0,0 +1,12 @@
package shadowsocks
import (
"v2ray.com/core/common/loader"
"v2ray.com/core/proxy/registry"
)
func init() {
// Must happen after config is initialized
registry.MustRegisterOutboundHandlerCreator(loader.GetType(new(ClientConfig)), new(ClientFactory))
registry.MustRegisterInboundHandlerCreator(loader.GetType(new(ServerConfig)), new(ServerFactory))
}

View File

@@ -100,3 +100,29 @@ func (this *ChunkReader) Read() (*alloc.Buffer, error) {
return buffer, nil
}
type ChunkWriter struct {
writer io.Writer
auth *Authenticator
}
func NewChunkWriter(writer io.Writer, auth *Authenticator) *ChunkWriter {
return &ChunkWriter{
writer: writer,
auth: auth,
}
}
func (this *ChunkWriter) Release() {
this.writer = nil
this.auth = nil
}
func (this *ChunkWriter) Write(payload *alloc.Buffer) error {
totalLength := payload.Len()
payload.SliceBack(AuthSize)
this.auth.Authenticate(payload.Value[:0], payload.Value[AuthSize:])
payload.PrependUint16(uint16(totalLength))
_, err := this.writer.Write(payload.Bytes())
return err
}

View File

@@ -22,3 +22,15 @@ func TestNormalChunkReading(t *testing.T) {
payload.PrependBytes(3, 4)
assert.Bytes(payload.Value).Equals([]byte{3, 4, 11, 12, 13, 14, 15, 16, 17, 18})
}
func TestNormalChunkWriting(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(512).Clear()
writer := NewChunkWriter(buffer, NewAuthenticator(ChunkKeyGenerator(
[]byte{21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36})))
err := writer.Write(alloc.NewLocalBuffer(256).Clear().Append([]byte{11, 12, 13, 14, 15, 16, 17, 18}))
assert.Error(err).IsNil()
assert.Bytes(buffer.Value).Equals([]byte{0, 8, 39, 228, 69, 96, 133, 39, 254, 26, 201, 70, 11, 12, 13, 14, 15, 16, 17, 18})
}

View File

@@ -2,141 +2,386 @@ package shadowsocks
import (
"bytes"
"crypto/rand"
"errors"
"io"
"v2ray.com/core/common/alloc"
"v2ray.com/core/common/log"
"v2ray.com/core/common/crypto"
v2io "v2ray.com/core/common/io"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/proxy"
"v2ray.com/core/transport"
"v2ray.com/core/common/protocol"
)
const (
Version = 1
RequestOptionOneTimeAuth = protocol.RequestOption(101)
AddrTypeIPv4 = 1
AddrTypeIPv6 = 4
AddrTypeDomain = 3
)
type Request struct {
Address v2net.Address
Port v2net.Port
OTA bool
UDPPayload *alloc.Buffer
}
func (this *Request) Release() {
this.Address = nil
if this.UDPPayload != nil {
this.UDPPayload.Release()
func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHeader, v2io.Reader, error) {
rawAccount, err := user.GetTypedAccount()
if err != nil {
return nil, nil, errors.New("Shadowsocks|TCP: Failed to parse account: " + err.Error())
}
}
account := rawAccount.(*ShadowsocksAccount)
func (this *Request) DetachUDPPayload() *alloc.Buffer {
payload := this.UDPPayload
this.UDPPayload = nil
return payload
}
func ReadRequest(reader io.Reader, auth *Authenticator, udp bool) (*Request, error) {
buffer := alloc.NewSmallBuffer()
buffer := alloc.NewLocalBuffer(256)
defer buffer.Release()
_, err := io.ReadFull(reader, buffer.Value[:1])
ivLen := account.Cipher.IVSize()
_, err = io.ReadFull(reader, buffer.Value[:ivLen])
if err != nil {
if err != io.EOF {
log.Warning("Shadowsocks: Failed to read address type: ", err)
return nil, transport.ErrCorruptedPacket
}
return nil, err
return nil, nil, errors.New("Shadowsocks|TCP: Failed to read IV: " + err.Error())
}
lenBuffer := 1
request := new(Request)
iv := append([]byte(nil), buffer.Value[:ivLen]...)
stream, err := account.Cipher.NewDecodingStream(account.Key, iv)
if err != nil {
return nil, nil, errors.New("Shadowsocks|TCP: Failed to initialize decoding stream: " + err.Error())
}
reader = crypto.NewCryptionReader(stream, reader)
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
request := &protocol.RequestHeader{
Version: Version,
User: user,
Command: protocol.RequestCommandTCP,
}
lenBuffer := 1
_, err = io.ReadFull(reader, buffer.Value[:1])
if err != nil {
return nil, nil, errors.New("Sahdowsocks|TCP: Failed to read address type: " + err.Error())
}
addrType := (buffer.Value[0] & 0x0F)
if (buffer.Value[0] & 0x10) == 0x10 {
request.OTA = true
request.Option |= RequestOptionOneTimeAuth
}
switch addrType {
case AddrTypeIPv4:
_, err := io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+4])
if err != nil {
log.Warning("Shadowsocks: Failed to read IPv4 address: ", err)
return nil, transport.ErrCorruptedPacket
return nil, nil, errors.New("Shadowsocks|TCP: Failed to read IPv4 address: " + err.Error())
}
request.Address = v2net.IPAddress(buffer.Value[lenBuffer : lenBuffer+4])
lenBuffer += 4
case AddrTypeIPv6:
_, err := io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+16])
if err != nil {
log.Warning("Shadowsocks: Failed to read IPv6 address: ", err)
return nil, transport.ErrCorruptedPacket
return nil, nil, errors.New("Shadowsocks|TCP: Failed to read IPv6 address: " + err.Error())
}
request.Address = v2net.IPAddress(buffer.Value[lenBuffer : lenBuffer+16])
lenBuffer += 16
case AddrTypeDomain:
_, err := io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+1])
if err != nil {
log.Warning("Shadowsocks: Failed to read domain lenth: ", err)
return nil, transport.ErrCorruptedPacket
return nil, nil, errors.New("Shadowsocks|TCP: Failed to read domain lenth: " + err.Error())
}
domainLength := int(buffer.Value[lenBuffer])
lenBuffer++
_, err = io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+domainLength])
if err != nil {
log.Warning("Shadowsocks: Failed to read domain: ", err)
return nil, transport.ErrCorruptedPacket
return nil, nil, errors.New("Shadowsocks|TCP: Failed to read domain: " + err.Error())
}
request.Address = v2net.DomainAddress(string(buffer.Value[lenBuffer : lenBuffer+domainLength]))
lenBuffer += domainLength
default:
log.Warning("Shadowsocks: Unknown address type: ", addrType)
return nil, transport.ErrCorruptedPacket
return nil, nil, errors.New("Shadowsocks|TCP: Unknown address type.")
}
_, err = io.ReadFull(reader, buffer.Value[lenBuffer:lenBuffer+2])
if err != nil {
log.Warning("Shadowsocks: Failed to read port: ", err)
return nil, transport.ErrCorruptedPacket
return nil, nil, errors.New("Shadowsocks|TCP: Failed to read port: " + err.Error())
}
request.Port = v2net.PortFromBytes(buffer.Value[lenBuffer : lenBuffer+2])
lenBuffer += 2
var authBytes []byte
if udp {
nBytes, err := reader.Read(buffer.Value[lenBuffer:])
if request.Option.Has(RequestOptionOneTimeAuth) {
authBytes := buffer.Value[lenBuffer : lenBuffer+AuthSize]
_, err = io.ReadFull(reader, authBytes)
if err != nil {
log.Warning("Shadowsocks: Failed to read UDP payload: ", err)
return nil, transport.ErrCorruptedPacket
return nil, nil, errors.New("Shadowsocks|TCP: Failed to read OTA: " + err.Error())
}
buffer.Slice(0, lenBuffer+nBytes)
if request.OTA {
authBytes = buffer.Value[lenBuffer+nBytes-AuthSize:]
request.UDPPayload = alloc.NewSmallBuffer().Clear().Append(buffer.Value[lenBuffer : lenBuffer+nBytes-AuthSize])
lenBuffer = lenBuffer + nBytes - AuthSize
} else {
request.UDPPayload = alloc.NewSmallBuffer().Clear().Append(buffer.Value[lenBuffer:])
}
} else {
if request.OTA {
authBytes = buffer.Value[lenBuffer : lenBuffer+AuthSize]
_, err = io.ReadFull(reader, authBytes)
if err != nil {
log.Warning("Shadowsocks: Failed to read OTA: ", err)
return nil, transport.ErrCorruptedPacket
}
}
}
if request.OTA {
actualAuth := auth.Authenticate(nil, buffer.Value[0:lenBuffer])
actualAuth := authenticator.Authenticate(nil, buffer.Value[0:lenBuffer])
if !bytes.Equal(actualAuth, authBytes) {
log.Warning("Shadowsocks: Invalid OTA.")
return nil, proxy.ErrInvalidAuthentication
return nil, nil, errors.New("Shadowsocks|TCP: Invalid OTA")
}
}
return request, nil
var chunkReader v2io.Reader
if request.Option.Has(RequestOptionOneTimeAuth) {
chunkReader = NewChunkReader(reader, NewAuthenticator(ChunkKeyGenerator(iv)))
} else {
chunkReader = v2io.NewAdaptiveReader(reader)
}
return request, chunkReader, nil
}
func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (v2io.Writer, error) {
user := request.User
rawAccount, err := user.GetTypedAccount()
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to parse account: " + err.Error())
}
account := rawAccount.(*ShadowsocksAccount)
iv := make([]byte, account.Cipher.IVSize())
rand.Read(iv)
_, err = writer.Write(iv)
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to write IV: " + err.Error())
}
stream, err := account.Cipher.NewEncodingStream(account.Key, iv)
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to create encoding stream: " + err.Error())
}
writer = crypto.NewCryptionWriter(stream, writer)
header := alloc.NewLocalBuffer(512).Clear()
switch request.Address.Family() {
case v2net.AddressFamilyIPv4:
header.AppendBytes(AddrTypeIPv4)
header.Append([]byte(request.Address.IP()))
case v2net.AddressFamilyIPv6:
header.AppendBytes(AddrTypeIPv6)
header.Append([]byte(request.Address.IP()))
case v2net.AddressFamilyDomain:
header.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain())))
header.Append([]byte(request.Address.Domain()))
default:
return nil, errors.New("Shadowsocks|TCP: Unsupported address type. ")
}
header.AppendUint16(uint16(request.Port))
if request.Option.Has(RequestOptionOneTimeAuth) {
header.Value[0] |= 0x10
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
header.Value = authenticator.Authenticate(header.Value, header.Value)
}
_, err = writer.Write(header.Value)
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to write header: " + err.Error())
}
var chunkWriter v2io.Writer
if request.Option.Has(RequestOptionOneTimeAuth) {
chunkWriter = NewChunkWriter(writer, NewAuthenticator(ChunkKeyGenerator(iv)))
} else {
chunkWriter = v2io.NewAdaptiveWriter(writer)
}
return chunkWriter, nil
}
func ReadTCPResponse(user *protocol.User, reader io.Reader) (v2io.Reader, error) {
rawAccount, err := user.GetTypedAccount()
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to parse account: " + err.Error())
}
account := rawAccount.(*ShadowsocksAccount)
iv := make([]byte, account.Cipher.IVSize())
_, err = io.ReadFull(reader, iv)
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to read IV: " + err.Error())
}
stream, err := account.Cipher.NewDecodingStream(account.Key, iv)
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to initialize decoding stream: " + err.Error())
}
return v2io.NewAdaptiveReader(crypto.NewCryptionReader(stream, reader)), nil
}
func WriteTCPResponse(request *protocol.RequestHeader, writer io.Writer) (v2io.Writer, error) {
user := request.User
rawAccount, err := user.GetTypedAccount()
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to parse account: " + err.Error())
}
account := rawAccount.(*ShadowsocksAccount)
iv := make([]byte, account.Cipher.IVSize())
rand.Read(iv)
_, err = writer.Write(iv)
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to write IV: " + err.Error())
}
stream, err := account.Cipher.NewEncodingStream(account.Key, iv)
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to create encoding stream: " + err.Error())
}
return v2io.NewAdaptiveWriter(crypto.NewCryptionWriter(stream, writer)), nil
}
func EncodeUDPPacket(request *protocol.RequestHeader, payload *alloc.Buffer) (*alloc.Buffer, error) {
user := request.User
rawAccount, err := user.GetTypedAccount()
if err != nil {
return nil, errors.New("Shadowsocks|UDP: Failed to parse account: " + err.Error())
}
account := rawAccount.(*ShadowsocksAccount)
buffer := alloc.NewLocalBuffer(2048)
ivLen := account.Cipher.IVSize()
buffer.Slice(0, ivLen)
rand.Read(buffer.Value)
iv := buffer.Value
switch request.Address.Family() {
case v2net.AddressFamilyIPv4:
buffer.AppendBytes(AddrTypeIPv4)
buffer.Append([]byte(request.Address.IP()))
case v2net.AddressFamilyIPv6:
buffer.AppendBytes(AddrTypeIPv6)
buffer.Append([]byte(request.Address.IP()))
case v2net.AddressFamilyDomain:
buffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain())))
buffer.Append([]byte(request.Address.Domain()))
default:
return nil, errors.New("Shadowsocks|UDP: Unsupported address type. ")
}
buffer.AppendUint16(uint16(request.Port))
buffer.Append(payload.Value)
if request.Option.Has(RequestOptionOneTimeAuth) {
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
buffer.Value[ivLen] |= 0x10
buffer.Value = authenticator.Authenticate(buffer.Value, buffer.Value[ivLen:])
}
stream, err := account.Cipher.NewEncodingStream(account.Key, iv)
if err != nil {
return nil, errors.New("Shadowsocks|TCP: Failed to create encoding stream: " + err.Error())
}
stream.XORKeyStream(buffer.Value[ivLen:], buffer.Value[ivLen:])
return buffer, nil
}
func DecodeUDPPacket(user *protocol.User, payload *alloc.Buffer) (*protocol.RequestHeader, *alloc.Buffer, error) {
rawAccount, err := user.GetTypedAccount()
if err != nil {
return nil, nil, errors.New("Shadowsocks|UDP: Failed to parse account: " + err.Error())
}
account := rawAccount.(*ShadowsocksAccount)
ivLen := account.Cipher.IVSize()
iv := payload.Value[:ivLen]
payload.SliceFrom(ivLen)
stream, err := account.Cipher.NewDecodingStream(account.Key, iv)
if err != nil {
return nil, nil, errors.New("Shadowsocks|UDP: Failed to initialize decoding stream: " + err.Error())
}
stream.XORKeyStream(payload.Value, payload.Value)
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
request := &protocol.RequestHeader{
Version: Version,
User: user,
Command: protocol.RequestCommandUDP,
}
addrType := (payload.Value[0] & 0x0F)
if (payload.Value[0] & 0x10) == 0x10 {
request.Option |= RequestOptionOneTimeAuth
}
if request.Option.Has(RequestOptionOneTimeAuth) {
payloadLen := payload.Len() - AuthSize
authBytes := payload.Value[payloadLen:]
actualAuth := authenticator.Authenticate(nil, payload.Value[0:payloadLen])
if !bytes.Equal(actualAuth, authBytes) {
return nil, nil, errors.New("Shadowsocks|UDP: Invalid OTA.")
}
payload.Slice(0, payloadLen)
}
payload.SliceFrom(1)
switch addrType {
case AddrTypeIPv4:
request.Address = v2net.IPAddress(payload.Value[:4])
payload.SliceFrom(4)
case AddrTypeIPv6:
request.Address = v2net.IPAddress(payload.Value[:16])
payload.SliceFrom(16)
case AddrTypeDomain:
domainLength := int(payload.Value[0])
request.Address = v2net.DomainAddress(string(payload.Value[1 : 1+domainLength]))
payload.SliceFrom(1 + domainLength)
default:
return nil, nil, errors.New("Shadowsocks|UDP: Unknown address type")
}
request.Port = v2net.PortFromBytes(payload.Value[:2])
payload.SliceFrom(2)
return request, payload, nil
}
type UDPReader struct {
Reader io.Reader
User *protocol.User
}
func (this *UDPReader) Read() (*alloc.Buffer, error) {
buffer := alloc.NewLocalBuffer(2048)
nBytes, err := this.Reader.Read(buffer.Value)
if err != nil {
buffer.Release()
return nil, err
}
buffer.Slice(0, nBytes)
_, payload, err := DecodeUDPPacket(this.User, buffer)
if err != nil {
buffer.Release()
return nil, err
}
return payload, nil
}
func (this *UDPReader) Release() {
}
type UDPWriter struct {
Writer io.Writer
Request *protocol.RequestHeader
}
func (this *UDPWriter) Write(buffer *alloc.Buffer) error {
payload, err := EncodeUDPPacket(this.Request, buffer)
if err != nil {
return err
}
_, err = this.Writer.Write(payload.Value)
payload.Release()
return err
}
func (this *UDPWriter) Release() {
}

View File

@@ -1,136 +1,118 @@
package shadowsocks_test
import (
"io"
"testing"
"v2ray.com/core/common/alloc"
"v2ray.com/core/common/loader"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/proxy"
"v2ray.com/core/common/protocol"
. "v2ray.com/core/proxy/shadowsocks"
"v2ray.com/core/testing/assert"
"v2ray.com/core/transport"
)
func TestNormalRequestParsing(t *testing.T) {
func TestUDPEncoding(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear()
buffer.AppendBytes(1, 127, 0, 0, 1, 0, 80)
request := &protocol.RequestHeader{
Version: Version,
Command: protocol.RequestCommandUDP,
Address: v2net.LocalHostIP,
Port: 1234,
User: &protocol.User{
Email: "love@v2ray.com",
Account: loader.NewTypedSettings(&Account{
Password: "shadowsocks-password",
CipherType: CipherType_AES_128_CFB,
Ota: Account_Disabled,
}),
},
}
request, err := ReadRequest(buffer, nil, false)
data := alloc.NewLocalBuffer(256).Clear().AppendString("test string")
encodedData, err := EncodeUDPPacket(request, data)
assert.Error(err).IsNil()
assert.Address(request.Address).Equals(v2net.LocalHostIP)
assert.Port(request.Port).Equals(v2net.Port(80))
assert.Bool(request.OTA).IsFalse()
}
func TestEmptyPayload(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear()
_, err := ReadRequest(buffer, nil, false)
assert.Error(err).Equals(io.EOF)
}
func TestSingleBytePayload(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(1)
_, err := ReadRequest(buffer, nil, false)
assert.Error(err).Equals(transport.ErrCorruptedPacket)
}
func TestWrongAddressType(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(5)
_, err := ReadRequest(buffer, nil, false)
assert.Error(err).Equals(transport.ErrCorruptedPacket)
}
func TestInsufficientAddressRequest(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(1, 1)
_, err := ReadRequest(buffer, nil, false)
assert.Error(err).Equals(transport.ErrCorruptedPacket)
buffer = alloc.NewLocalBuffer(2048).Clear().AppendBytes(4, 1)
_, err = ReadRequest(buffer, nil, false)
assert.Error(err).Equals(transport.ErrCorruptedPacket)
buffer = alloc.NewLocalBuffer(2048).Clear().AppendBytes(3, 255, 1)
_, err = ReadRequest(buffer, nil, false)
assert.Error(err).Equals(transport.ErrCorruptedPacket)
}
func TestInsufficientPortRequest(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(1, 1, 2, 3, 4, 5)
_, err := ReadRequest(buffer, nil, false)
assert.Error(err).Equals(transport.ErrCorruptedPacket)
}
func TestOTARequest(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear()
buffer.AppendBytes(0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0, 239, 115, 52, 212, 178, 172, 26, 6, 168, 0)
auth := NewAuthenticator(HeaderKeyGenerator(
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5},
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5}))
request, err := ReadRequest(buffer, auth, false)
decodedRequest, decodedData, err := DecodeUDPPacket(request.User, encodedData)
assert.Error(err).IsNil()
assert.Address(request.Address).Equals(v2net.DomainAddress("www.v2ray.com"))
assert.Bool(request.OTA).IsTrue()
assert.Bytes(decodedData.Value).Equals(data.Value)
assert.Address(decodedRequest.Address).Equals(request.Address)
assert.Port(decodedRequest.Port).Equals(request.Port)
}
func TestInvalidOTARequest(t *testing.T) {
func TestTCPRequest(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear()
buffer.AppendBytes(0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0, 239, 115, 52, 212, 178, 172, 26, 6, 168, 1)
request := &protocol.RequestHeader{
Version: Version,
Command: protocol.RequestCommandTCP,
Address: v2net.LocalHostIP,
Option: RequestOptionOneTimeAuth,
Port: 1234,
User: &protocol.User{
Email: "love@v2ray.com",
Account: loader.NewTypedSettings(&Account{
Password: "tcp-password",
CipherType: CipherType_CHACHA20,
}),
},
}
auth := NewAuthenticator(HeaderKeyGenerator(
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5},
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5}))
_, err := ReadRequest(buffer, auth, false)
assert.Error(err).Equals(proxy.ErrInvalidAuthentication)
}
data := alloc.NewLocalBuffer(256).Clear().AppendString("test string")
cache := alloc.NewLargeBuffer().Clear()
func TestUDPRequestParsing(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear()
buffer.AppendBytes(1, 127, 0, 0, 1, 0, 80, 1, 2, 3, 4, 5, 6)
request, err := ReadRequest(buffer, nil, true)
writer, err := WriteTCPRequest(request, cache)
assert.Error(err).IsNil()
assert.Address(request.Address).Equals(v2net.LocalHostIP)
assert.Port(request.Port).Equals(v2net.Port(80))
assert.Bool(request.OTA).IsFalse()
assert.Bytes(request.UDPPayload.Value).Equals([]byte{1, 2, 3, 4, 5, 6})
writer.Write(data)
decodedRequest, reader, err := ReadTCPSession(request.User, cache)
assert.Error(err).IsNil()
assert.Address(decodedRequest.Address).Equals(request.Address)
assert.Port(decodedRequest.Port).Equals(request.Port)
decodedData, err := reader.Read()
assert.Error(err).IsNil()
assert.Bytes(decodedData.Value).Equals([]byte("test string"))
}
func TestUDPRequestWithOTA(t *testing.T) {
func TestUDPReaderWriter(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewLocalBuffer(2048).Clear()
buffer.AppendBytes(
0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
58, 32, 223, 30, 57, 199, 50, 139, 143, 101)
user := &protocol.User{
Account: loader.NewTypedSettings(&Account{
Password: "test-password",
CipherType: CipherType_CHACHA20_IEFT,
}),
}
cache := alloc.NewBuffer().Clear()
writer := &UDPWriter{
Writer: cache,
Request: &protocol.RequestHeader{
Version: Version,
Address: v2net.DomainAddress("v2ray.com"),
Port: 123,
User: user,
Option: RequestOptionOneTimeAuth,
},
}
auth := NewAuthenticator(HeaderKeyGenerator(
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5},
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5}))
request, err := ReadRequest(buffer, auth, true)
reader := &UDPReader{
Reader: cache,
User: user,
}
err := writer.Write(alloc.NewBuffer().Clear().AppendString("test payload"))
assert.Error(err).IsNil()
assert.Address(request.Address).Equals(v2net.DomainAddress("www.v2ray.com"))
assert.Bool(request.OTA).IsTrue()
assert.Bytes(request.UDPPayload.Value).Equals([]byte{
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0})
payload, err := reader.Read()
assert.Error(err).IsNil()
assert.String(payload.String()).Equals("test payload")
err = writer.Write(alloc.NewBuffer().Clear().AppendString("test payload 2"))
assert.Error(err).IsNil()
payload, err = reader.Read()
assert.Error(err).IsNil()
assert.String(payload.String()).Equals("test payload 2")
}

View File

@@ -2,22 +2,18 @@
package shadowsocks
import (
"crypto/rand"
"io"
"sync"
"errors"
"v2ray.com/core/app"
"v2ray.com/core/app/dispatcher"
"v2ray.com/core/common"
"v2ray.com/core/common/alloc"
"v2ray.com/core/common/crypto"
v2io "v2ray.com/core/common/io"
"v2ray.com/core/common/loader"
"v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/proxy"
"v2ray.com/core/proxy/registry"
"v2ray.com/core/transport/internet"
"v2ray.com/core/transport/internet/udp"
)
@@ -25,8 +21,8 @@ import (
type Server struct {
packetDispatcher dispatcher.PacketDispatcher
config *ServerConfig
cipher Cipher
cipherKey []byte
user *protocol.User
account *ShadowsocksAccount
meta *proxy.InboundHandlerMeta
accepting bool
tcpHub *internet.TCPHub
@@ -38,23 +34,18 @@ func NewServer(config *ServerConfig, space app.Space, meta *proxy.InboundHandler
if config.GetUser() == nil {
return nil, protocol.ErrUserMissing
}
rawAccount, err := config.GetUser().GetTypedAccount()
rawAccount, err := config.User.GetTypedAccount()
if err != nil {
return nil, err
}
account, ok := rawAccount.(*Account)
if !ok {
return nil, protocol.ErrUnknownAccountType
}
cipher, err := account.GetCipher()
if err != nil {
return nil, err
return nil, errors.New("Shadowsocks|Server: Failed to get user account: " + err.Error())
}
account := rawAccount.(*ShadowsocksAccount)
s := &Server{
config: config,
meta: meta,
cipher: cipher,
cipherKey: account.GetCipherKey(),
config: config,
meta: meta,
user: config.GetUser(),
account: account,
}
space.InitializeApplication(func() error {
@@ -84,7 +75,6 @@ func (this *Server) Close() {
this.udpHub.Close()
this.udpHub = nil
}
}
func (this *Server) Start() error {
@@ -115,81 +105,48 @@ func (this *Server) Start() error {
}
func (this *Server) handlerUDPPayload(payload *alloc.Buffer, session *proxy.SessionInfo) {
defer payload.Release()
source := session.Source
ivLen := this.cipher.IVSize()
iv := payload.Value[:ivLen]
payload.SliceFrom(ivLen)
stream, err := this.cipher.NewDecodingStream(this.cipherKey, iv)
request, data, err := DecodeUDPPacket(this.user, payload)
if err != nil {
log.Error("Shadowsocks: Failed to create decoding stream: ", err)
log.Info("Shadowsocks|Server: Skipping invalid UDP packet from: ", source, ": ", err)
log.Access(source, "", log.AccessRejected, err)
payload.Release()
return
}
reader := crypto.NewCryptionReader(stream, payload)
request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(this.cipherKey, iv)), true)
if err != nil {
if err != io.EOF {
log.Access(source, "", log.AccessRejected, err)
log.Warning("Shadowsocks: Invalid request from ", source, ": ", err)
}
if request.Option.Has(RequestOptionOneTimeAuth) && this.account.OneTimeAuth == Account_Disabled {
log.Info("Shadowsocks|Server: Client payload enables OTA but server doesn't allow it.")
payload.Release()
return
}
//defer request.Release()
dest := v2net.UDPDestination(request.Address, request.Port)
if !request.Option.Has(RequestOptionOneTimeAuth) && this.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: Tunnelling request to ", dest)
log.Info("Shadowsocks|Server: Tunnelling request to ", dest)
this.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: dest}, request.DetachUDPPayload(), func(destination v2net.Destination, payload *alloc.Buffer) {
this.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: dest, User: request.User}, data, func(destination v2net.Destination, payload *alloc.Buffer) {
defer payload.Release()
response := alloc.NewBuffer().Slice(0, ivLen)
defer response.Release()
rand.Read(response.Value)
respIv := response.Value
stream, err := this.cipher.NewEncodingStream(this.cipherKey, respIv)
data, err := EncodeUDPPacket(request, payload)
if err != nil {
log.Error("Shadowsocks: Failed to create encoding stream: ", err)
log.Warning("Shadowsocks|Server: Failed to encode UDP packet: ", err)
return
}
defer data.Release()
writer := crypto.NewCryptionWriter(stream, response)
switch request.Address.Family() {
case v2net.AddressFamilyIPv4:
writer.Write([]byte{AddrTypeIPv4})
writer.Write(request.Address.IP())
case v2net.AddressFamilyIPv6:
writer.Write([]byte{AddrTypeIPv6})
writer.Write(request.Address.IP())
case v2net.AddressFamilyDomain:
writer.Write([]byte{AddrTypeDomain, byte(len(request.Address.Domain()))})
writer.Write([]byte(request.Address.Domain()))
}
writer.Write(request.Port.Bytes(nil))
writer.Write(payload.Value)
if request.OTA {
respAuth := NewAuthenticator(HeaderKeyGenerator(this.cipherKey, respIv))
respAuth.Authenticate(response.Value, response.Value[ivLen:])
}
this.udpHub.WriteTo(response.Value, source)
this.udpHub.WriteTo(data.Value, source)
})
}
func (this *Server) handleConnection(conn internet.Connection) {
defer conn.Close()
buffer := alloc.NewSmallBuffer()
defer buffer.Release()
conn.SetReusable(false)
timedReader := v2net.NewTimeOutReader(16, conn)
defer timedReader.Release()
@@ -197,86 +154,55 @@ func (this *Server) handleConnection(conn internet.Connection) {
bufferedReader := v2io.NewBufferedReader(timedReader)
defer bufferedReader.Release()
ivLen := this.cipher.IVSize()
_, err := io.ReadFull(bufferedReader, buffer.Value[:ivLen])
if err != nil {
if err != io.EOF {
log.Access(conn.RemoteAddr(), "", log.AccessRejected, err)
log.Warning("Shadowsocks: Failed to read IV: ", err)
}
return
}
iv := buffer.Value[:ivLen]
stream, err := this.cipher.NewDecodingStream(this.cipherKey, iv)
if err != nil {
log.Error("Shadowsocks: Failed to create decoding stream: ", err)
return
}
reader := crypto.NewCryptionReader(stream, bufferedReader)
request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(this.cipherKey, iv)), false)
request, bodyReader, err := ReadTCPSession(this.user, bufferedReader)
if err != nil {
log.Access(conn.RemoteAddr(), "", log.AccessRejected, err)
log.Warning("Shadowsocks: Invalid request from ", conn.RemoteAddr(), ": ", err)
log.Info("Shadowsocks|Server: Failed to create request from: ", conn.RemoteAddr(), ": ", err)
return
}
defer request.Release()
defer bodyReader.Release()
bufferedReader.SetCached(false)
userSettings := this.config.GetUser().GetSettings()
userSettings := this.user.GetSettings()
timedReader.SetTimeOut(userSettings.PayloadReadTimeout)
dest := v2net.TCPDestination(request.Address, request.Port)
dest := request.Destination()
log.Access(conn.RemoteAddr(), dest, log.AccessAccepted, "")
log.Info("Shadowsocks: Tunnelling request to ", dest)
log.Info("Shadowsocks|Server: Tunnelling request to ", dest)
ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{
Source: v2net.DestinationFromAddr(conn.RemoteAddr()),
Destination: dest,
User: request.User,
})
defer ray.InboundOutput().Release()
var writeFinish sync.Mutex
writeFinish.Lock()
go func() {
if payload, err := ray.InboundOutput().Read(); err == nil {
payload.SliceBack(ivLen)
rand.Read(payload.Value[:ivLen])
defer writeFinish.Unlock()
stream, err := this.cipher.NewEncodingStream(this.cipherKey, payload.Value[:ivLen])
if err != nil {
log.Error("Shadowsocks: Failed to create encoding stream: ", err)
return
}
stream.XORKeyStream(payload.Value[ivLen:], payload.Value[ivLen:])
bufferedWriter := v2io.NewBufferedWriter(conn)
defer bufferedWriter.Release()
conn.Write(payload.Value)
payload.Release()
writer := crypto.NewCryptionWriter(stream, conn)
v2writer := v2io.NewAdaptiveWriter(writer)
v2io.Pipe(ray.InboundOutput(), v2writer)
writer.Release()
v2writer.Release()
responseWriter, err := WriteTCPResponse(request, bufferedWriter)
if err != nil {
log.Warning("Shadowsocks|Server: Failed to write response: ", err)
return
}
defer responseWriter.Release()
if payload, err := ray.InboundOutput().Read(); err == nil {
responseWriter.Write(payload)
bufferedWriter.SetCached(false)
v2io.Pipe(ray.InboundOutput(), responseWriter)
}
writeFinish.Unlock()
}()
var payloadReader v2io.Reader
if request.OTA {
payloadAuth := NewAuthenticator(ChunkKeyGenerator(iv))
payloadReader = NewChunkReader(reader, payloadAuth)
} else {
payloadReader = v2io.NewAdaptiveReader(reader)
}
v2io.Pipe(payloadReader, ray.InboundInput())
v2io.Pipe(bodyReader, ray.InboundInput())
ray.InboundInput().Close()
payloadReader.Release()
writeFinish.Lock()
}
@@ -285,7 +211,7 @@ type ServerFactory struct{}
func (this *ServerFactory) StreamCapability() v2net.NetworkList {
return v2net.NetworkList{
Network: []v2net.Network{v2net.Network_RawTCP},
Network: []v2net.Network{v2net.Network_TCP, v2net.Network_RawTCP},
}
}
@@ -295,7 +221,3 @@ func (this *ServerFactory) Create(space app.Space, rawConfig interface{}, meta *
}
return NewServer(rawConfig.(*ServerConfig), space, meta)
}
func init() {
registry.MustRegisterInboundHandlerCreator(loader.GetType(new(ServerConfig)), new(ServerFactory))
}

View File

@@ -0,0 +1,5 @@
package shadowsocks_test
import (
// . "v2ray.com/core/proxy/shadowsocks"
)

View File

@@ -120,7 +120,7 @@ func (request Socks5UserPassRequest) AuthDetail() string {
}
func ReadUserPassRequest(reader io.Reader) (request Socks5UserPassRequest, err error) {
buffer := alloc.NewSmallBuffer()
buffer := alloc.NewLocalBuffer(512)
defer buffer.Release()
_, err = reader.Read(buffer.Value[0:2])
@@ -186,7 +186,7 @@ type Socks5Request struct {
}
func ReadRequest(reader io.Reader) (request *Socks5Request, err error) {
buffer := alloc.NewSmallBuffer()
buffer := alloc.NewLocalBuffer(512)
defer buffer.Release()
_, err = io.ReadFull(reader, buffer.Value[:4])

View File

@@ -12,10 +12,6 @@ type InternalAccount struct {
AlterIDs []*protocol.ID
}
func NewAccount() protocol.AsAccount {
return &Account{}
}
func (this *InternalAccount) AnyValidID() *protocol.ID {
if len(this.AlterIDs) == 0 {
return this.ID

View File

@@ -1,22 +0,0 @@
// +build json
package vmess
import (
"encoding/json"
)
func (u *Account) UnmarshalJSON(data []byte) error {
type JsonConfig struct {
ID string `json:"id"`
AlterIds uint16 `json:"alterId"`
}
var rawConfig JsonConfig
if err := json.Unmarshal(data, &rawConfig); err != nil {
return err
}
u.Id = rawConfig.ID
u.AlterId = uint32(rawConfig.AlterIds)
return nil
}

View File

@@ -169,6 +169,7 @@ func (this *VMessInboundHandler) HandleConnection(connection internet.Connection
ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{
Source: v2net.DestinationFromAddr(connection.RemoteAddr()),
Destination: request.Destination(),
User: request.User,
})
input := ray.InboundInput()
output := ray.InboundOutput()

View File

@@ -22,7 +22,7 @@ func (this *VMessOutboundHandler) handleSwitchAccount(cmd *protocol.CommandSwitc
}
dest := v2net.TCPDestination(cmd.Host, cmd.Port)
until := time.Now().Add(time.Duration(cmd.ValidMin) * time.Minute)
this.serverList.AddServer(protocol.NewServerSpec(vmess.NewAccount, dest, protocol.BeforeTime(until), user))
this.serverList.AddServer(protocol.NewServerSpec(dest, protocol.BeforeTime(until), user))
}
func (this *VMessOutboundHandler) handleCommand(dest v2net.Destination, cmd protocol.ResponseCommand) {

View File

@@ -14,7 +14,6 @@ import (
"v2ray.com/core/common/retry"
"v2ray.com/core/proxy"
"v2ray.com/core/proxy/registry"
"v2ray.com/core/proxy/vmess"
"v2ray.com/core/proxy/vmess/encoding"
vmessio "v2ray.com/core/proxy/vmess/io"
"v2ray.com/core/transport/internet"
@@ -36,7 +35,9 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al
err := retry.Timed(5, 100).On(func() error {
rec = this.serverPicker.PickServer()
rawConn, err := internet.Dial(this.meta.Address, rec.Destination(), this.meta.StreamSettings)
rawConn, err := internet.Dial(this.meta.Address, rec.Destination(), internet.DialerOptions{
Stream: this.meta.StreamSettings,
})
if err != nil {
return err
}
@@ -172,7 +173,7 @@ func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.
serverList := protocol.NewServerList()
for _, rec := range vOutConfig.Receiver {
serverList.AddServer(protocol.NewServerSpecFromPB(vmess.NewAccount, *rec))
serverList.AddServer(protocol.NewServerSpecFromPB(*rec))
}
handler := &VMessOutboundHandler{
serverList: serverList,

View File

@@ -1,5 +1,9 @@
#!/bin/bash
if [ -n "${TRAVIS_TAG}" ]; then
exit 0
fi
FAIL=0
V2RAY_OUT=${GOPATH}/out/v2ray

View File

@@ -0,0 +1,39 @@
{
"log": {
"loglevel": "debug"
},
"inbound": {
"port": 50050,
"listen": "127.0.0.1",
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1",
"port": 50052,
"network": "tcp,udp",
"timeout": 0
}
},
"outbound": {
"protocol": "shadowsocks",
"streamSettings": {
"network": "tcp",
"tcpSettings": {
"header": {
"type": "http",
"request": {},
"response": {}
}
}
},
"settings": {
"servers": [
{
"address": "127.0.0.1",
"port": 50051,
"method": "aes-256-cfb",
"password": "v2ray-password"
}
]
}
}
}

View File

@@ -1,11 +1,24 @@
{
"port": 50051,
"log": {
"loglevel": "debug"
},
"inbound": {
"port": 50051,
"listen": "127.0.0.1",
"protocol": "shadowsocks",
"settings": {
"method": "aes-256-cfb",
"password": "v2ray-password"
},
"streamSettings": {
"network": "tcp",
"tcpSettings": {
"header": {
"type": "http",
"request": {},
"response": {}
}
}
}
},
"inboundDetour": [
@@ -17,6 +30,16 @@
"method": "aes-128-cfb",
"password": "v2ray-another",
"udp": true
},
"streamSettings": {
"network": "tcp",
"tcpSettings": {
"header": {
"type": "http",
"request": {},
"response": {}
}
}
}
},
{

View File

@@ -9,15 +9,7 @@ import (
"v2ray.com/core/common/log"
// The following are necessary as they register handlers in their init functions.
_ "v2ray.com/core/proxy/blackhole"
_ "v2ray.com/core/proxy/dokodemo"
_ "v2ray.com/core/proxy/freedom"
_ "v2ray.com/core/proxy/http"
_ "v2ray.com/core/proxy/shadowsocks"
_ "v2ray.com/core/proxy/socks"
_ "v2ray.com/core/proxy/vmess/inbound"
_ "v2ray.com/core/proxy/vmess/outbound"
_ "v2ray.com/core"
)
var (

View File

@@ -4,11 +4,10 @@ import (
"net"
"testing"
"v2ray.com/core/common/alloc"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/testing/assert"
"v2ray.com/core/testing/servers/tcp"
ssclient "github.com/shadowsocks/shadowsocks-go/shadowsocks"
)
func TestShadowsocksTCP(t *testing.T) {
@@ -27,65 +26,39 @@ func TestShadowsocksTCP(t *testing.T) {
assert.Error(err).IsNil()
defer tcpServer.Close()
assert.Error(InitializeServerServer("test_6")).IsNil()
assert.Error(InitializeServerSetOnce("test_6")).IsNil()
cipher, err := ssclient.NewCipher("aes-256-cfb", "v2ray-password")
assert.Error(err).IsNil()
for i := 0; i < 1; i++ {
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: 50050,
})
assert.Error(err).IsNil()
rawAddr := []byte{1, 127, 0, 0, 1, 0xc3, 0x84} // 127.0.0.1:50052
conn, err := ssclient.DialWithRawAddr(rawAddr, "127.0.0.1:50051", cipher)
assert.Error(err).IsNil()
payload := "dokodemo request."
nBytes, err := conn.Write([]byte(payload))
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(payload))
payload := "shadowsocks request."
nBytes, err := conn.Write([]byte(payload))
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(payload))
conn.CloseWrite()
conn.Conn.(*net.TCPConn).CloseWrite()
response := alloc.NewBuffer().Clear()
finished := false
for {
_, err := response.FillFrom(conn)
assert.Error(err).IsNil()
if err != nil {
break
}
if response.String() == "Processed: "+payload {
finished = true
break
}
}
assert.Bool(finished).IsTrue()
response := make([]byte, 1024)
nBytes, err = conn.Read(response)
assert.Error(err).IsNil()
assert.String("Processed: " + payload).Equals(string(response[:nBytes]))
conn.Close()
cipher, err = ssclient.NewCipher("aes-128-cfb", "v2ray-another")
assert.Error(err).IsNil()
conn, err = ssclient.DialWithRawAddr(rawAddr, "127.0.0.1:50055", cipher)
assert.Error(err).IsNil()
payload = "shadowsocks request 2."
nBytes, err = conn.Write([]byte(payload))
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(payload))
conn.Conn.(*net.TCPConn).CloseWrite()
response = make([]byte, 1024)
nBytes, err = conn.Read(response)
assert.Error(err).IsNil()
assert.String("Processed: " + payload).Equals(string(response[:nBytes]))
conn.Close()
cipher, err = ssclient.NewCipher("chacha20", "new-password")
assert.Error(err).IsNil()
conn, err = ssclient.DialWithRawAddr(rawAddr, "127.0.0.1:50056", cipher)
assert.Error(err).IsNil()
payload = "shadowsocks request 3."
nBytes, err = conn.Write([]byte(payload))
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(payload))
conn.Conn.(*net.TCPConn).CloseWrite()
response = make([]byte, 1024)
nBytes, err = conn.Read(response)
assert.Error(err).IsNil()
assert.String("Processed: " + payload).Equals(string(response[:nBytes]))
conn.Close()
conn.Close()
}
CloseAllServers()
}

View File

@@ -16,7 +16,11 @@ func (this *LogConfig) Build() *log.Config {
if this == nil {
return nil
}
config := new(log.Config)
config := &log.Config{
ErrorLogType: log.LogType_Console,
AccessLogType: log.LogType_Console,
}
if len(this.AccessLog) > 0 {
config.AccessLogPath = this.AccessLog
config.AccessLogType = log.LogType_File
@@ -36,6 +40,7 @@ func (this *LogConfig) Build() *log.Config {
config.ErrorLogLevel = log.LogLevel_Error
case "none":
config.ErrorLogType = log.LogType_None
config.AccessLogType = log.LogType_None
default:
config.ErrorLogLevel = log.LogLevel_Warning
}

View File

@@ -10,6 +10,8 @@ import (
"v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/tools/geoip"
"github.com/golang/protobuf/proto"
)
type RouterRulesConfig struct {
@@ -48,7 +50,7 @@ type RouterRule struct {
OutboundTag string `json:"outboundTag"`
}
func parseIP(s string) *router.IP {
func parseIP(s string) *router.CIDR {
var addr, mask string
i := strings.Index(s, "/")
if i < 0 {
@@ -58,34 +60,56 @@ func parseIP(s string) *router.IP {
mask = s[i+1:]
}
ip := v2net.ParseAddress(addr)
if !ip.Family().Either(v2net.AddressFamilyIPv4, v2net.AddressFamilyIPv6) {
return nil
}
bits := uint32(32)
if len(mask) > 0 {
bits64, err := strconv.ParseUint(mask, 10, 32)
if err != nil {
switch ip.Family() {
case v2net.AddressFamilyIPv4:
bits := uint32(32)
if len(mask) > 0 {
bits64, err := strconv.ParseUint(mask, 10, 32)
if err != nil {
return nil
}
bits = uint32(bits64)
}
if bits > 32 {
log.Warning("Router: invalid network mask: ", bits)
return nil
}
bits = uint32(bits64)
}
if bits > 32 {
log.Warning("Router: invalid network mask: ", bits)
return &router.CIDR{
Ip: []byte(ip.IP()),
Prefix: bits,
}
case v2net.AddressFamilyIPv6:
bits := uint32(128)
if len(mask) > 0 {
bits64, err := strconv.ParseUint(mask, 10, 32)
if err != nil {
return nil
}
bits = uint32(bits64)
}
if bits > 128 {
log.Warning("Router: invalid network mask: ", bits)
return nil
}
return &router.CIDR{
Ip: []byte(ip.IP()),
Prefix: bits,
}
default:
log.Warning("Router: unsupported address: ", s)
return nil
}
return &router.IP{
Ip: []byte(ip.IP()),
UnmatchingBits: 32 - bits,
}
}
func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
type RawFieldRule struct {
RouterRule
Domain *StringList `json:"domain"`
IP *StringList `json:"ip"`
Port *PortRange `json:"port"`
Network *NetworkList `json:"network"`
Domain *StringList `json:"domain"`
IP *StringList `json:"ip"`
Port *PortRange `json:"port"`
Network *NetworkList `json:"network"`
SourceIP *StringList `json:"source"`
User *StringList `json:"user"`
}
rawFieldRule := new(RawFieldRule)
err := json.Unmarshal(msg, rawFieldRule)
@@ -114,7 +138,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
for _, ip := range *rawFieldRule.IP {
ipRule := parseIP(ip)
if ipRule != nil {
rule.Ip = append(rule.Ip, ipRule)
rule.Cidr = append(rule.Cidr, ipRule)
}
}
}
@@ -127,6 +151,21 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
rule.NetworkList = rawFieldRule.Network.Build()
}
if rawFieldRule.SourceIP != nil {
for _, ip := range *rawFieldRule.SourceIP {
ipRule := parseIP(ip)
if ipRule != nil {
rule.SourceCidr = append(rule.SourceCidr, ipRule)
}
}
}
if rawFieldRule.User != nil {
for _, s := range *rawFieldRule.User {
rule.UserEmail = append(rule.UserEmail, s)
}
}
return rule, nil
}
@@ -173,9 +212,13 @@ func parseChinaIPRule(data []byte) (*router.RoutingRule, error) {
log.Error("Router: Invalid router rule: ", err)
return nil, err
}
var chinaIPs geoip.CountryIPRange
if err := proto.Unmarshal(geoip.ChinaIPs, &chinaIPs); err != nil {
return nil, err
}
return &router.RoutingRule{
Tag: rawRule.OutboundTag,
Ip: geoip.ChinaIPs,
Tag: rawRule.OutboundTag,
Cidr: chinaIPs.Ips,
}, nil
}

View File

@@ -5,6 +5,7 @@ import (
"testing"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/proxy"
"v2ray.com/core/testing/assert"
. "v2ray.com/core/tools/conf"
)
@@ -27,12 +28,22 @@ func TestChinaIPJson(t *testing.T) {
assert.String(rule.Tag).Equals("x")
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(makeDestination("121.14.1.189"))).IsTrue() // sina.com.cn
assert.Bool(cond.Apply(makeDestination("101.226.103.106"))).IsTrue() // qq.com
assert.Bool(cond.Apply(makeDestination("115.239.210.36"))).IsTrue() // image.baidu.com
assert.Bool(cond.Apply(makeDestination("120.135.126.1"))).IsTrue()
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(makeDestination("8.8.8.8"))).IsFalse()
assert.Bool(cond.Apply(&proxy.SessionInfo{
Destination: v2net.TCPDestination(v2net.ParseAddress("8.8.8.8"), 80),
})).IsFalse()
}
func TestChinaSitesJson(t *testing.T) {
@@ -45,12 +56,22 @@ func TestChinaSitesJson(t *testing.T) {
assert.String(rule.Tag).Equals("y")
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(makeDomainDestination("v.qq.com"))).IsTrue()
assert.Bool(cond.Apply(makeDomainDestination("www.163.com"))).IsTrue()
assert.Bool(cond.Apply(makeDomainDestination("ngacn.cc"))).IsTrue()
assert.Bool(cond.Apply(makeDomainDestination("12306.cn"))).IsTrue()
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(makeDomainDestination("v2ray.com"))).IsFalse()
assert.Bool(cond.Apply(&proxy.SessionInfo{
Destination: v2net.TCPDestination(v2net.ParseAddress("v2ray.com"), 80),
})).IsFalse()
}
func TestDomainRule(t *testing.T) {
@@ -69,11 +90,21 @@ func TestDomainRule(t *testing.T) {
assert.Pointer(rule).IsNotNil()
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsTrue()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.aabb.com"), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.12306.cn"), 80))).IsTrue()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.acn.com"), 80))).IsFalse()
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()
}
func TestIPRule(t *testing.T) {
@@ -91,8 +122,44 @@ func TestIPRule(t *testing.T) {
assert.Pointer(rule).IsNotNil()
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80))).IsTrue()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80))).IsTrue()
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()
}
func TestSourceIPRule(t *testing.T) {
assert := assert.On(t)
rule := ParseRule([]byte(`{
"type": "field",
"source": [
"10.0.0.0/8",
"192.0.0.0/24"
],
"outboundTag": "direct"
}`))
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()
}

View File

@@ -26,6 +26,7 @@ func (this *ShadowsocksServerConfig) Build() (*loader.TypedSettings, error) {
}
account := &shadowsocks.Account{
Password: this.Password,
Ota: shadowsocks.Account_Auto,
}
cipher := strings.ToLower(this.Cipher)
switch cipher {
@@ -49,3 +50,74 @@ func (this *ShadowsocksServerConfig) Build() (*loader.TypedSettings, error) {
return loader.NewTypedSettings(config), nil
}
type ShadowsocksServerTarget struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Cipher string `json:"method"`
Password string `json:"password"`
Email string `json:"email"`
Ota bool `json:"ota"`
}
type ShadowsocksClientConfig struct {
Servers []*ShadowsocksServerTarget `json:"servers"`
}
func (this *ShadowsocksClientConfig) Build() (*loader.TypedSettings, error) {
config := new(shadowsocks.ClientConfig)
if len(this.Servers) == 0 {
return nil, errors.New("0 Shadowsocks server configured.")
}
serverSpecs := make([]*protocol.ServerEndpoint, len(this.Servers))
for idx, server := range this.Servers {
if server.Address == nil {
return nil, errors.New("Shadowsocks server address is not set.")
}
if server.Port == 0 {
return nil, errors.New("Invalid Shadowsocks port.")
}
if len(server.Password) == 0 {
return nil, errors.New("Shadowsocks password is not specified.")
}
account := &shadowsocks.Account{
Password: server.Password,
Ota: shadowsocks.Account_Enabled,
}
if !server.Ota {
account.Ota = shadowsocks.Account_Disabled
}
cipher := strings.ToLower(server.Cipher)
switch cipher {
case "aes-256-cfb":
account.CipherType = shadowsocks.CipherType_AES_256_CFB
case "aes-128-cfb":
account.CipherType = shadowsocks.CipherType_AES_128_CFB
case "chacha20":
account.CipherType = shadowsocks.CipherType_CHACHA20
case "chacha20-ietf":
account.CipherType = shadowsocks.CipherType_CHACHA20_IEFT
default:
return nil, errors.New("Unknown cipher method: " + cipher)
}
ss := &protocol.ServerEndpoint{
Address: server.Address.Build(),
Port: uint32(server.Port),
User: []*protocol.User{
{
Email: server.Email,
Account: loader.NewTypedSettings(account),
},
},
}
serverSpecs[idx] = ss
}
config.Server = serverSpecs
return loader.NewTypedSettings(config), nil
}

View File

@@ -30,11 +30,9 @@ func TestShadowsocksServerConfigParsing(t *testing.T) {
rawAccount, err := config.User.GetTypedAccount()
assert.Error(err).IsNil()
account, ok := rawAccount.(*shadowsocks.Account)
account, ok := rawAccount.(*shadowsocks.ShadowsocksAccount)
assert.Bool(ok).IsTrue()
cipher, err := account.GetCipher()
assert.Error(err).IsNil()
assert.Int(cipher.KeySize()).Equals(16)
assert.Bytes(account.GetCipherKey()).Equals([]byte{160, 224, 26, 2, 22, 110, 9, 80, 65, 52, 80, 20, 38, 243, 224, 241})
assert.Int(account.Cipher.KeySize()).Equals(16)
assert.Bytes(account.Key).Equals([]byte{160, 224, 26, 2, 22, 110, 9, 80, 65, 52, 80, 20, 38, 243, 224, 241})
}

View File

@@ -1,7 +1,10 @@
package conf
import (
"errors"
"v2ray.com/core/common/loader"
"v2ray.com/core/transport/internet/authenticators/http"
"v2ray.com/core/transport/internet/authenticators/noop"
"v2ray.com/core/transport/internet/authenticators/srtp"
"v2ray.com/core/transport/internet/authenticators/utp"
@@ -13,6 +16,12 @@ func (NoOpAuthenticator) Build() (*loader.TypedSettings, error) {
return loader.NewTypedSettings(new(noop.Config)), nil
}
type NoOpConnectionAuthenticator struct{}
func (NoOpConnectionAuthenticator) Build() (*loader.TypedSettings, error) {
return loader.NewTypedSettings(new(noop.Config)), nil
}
type SRTPAuthenticator struct{}
func (SRTPAuthenticator) Build() (*loader.TypedSettings, error) {
@@ -24,3 +33,152 @@ type UTPAuthenticator struct{}
func (UTPAuthenticator) Build() (*loader.TypedSettings, error) {
return loader.NewTypedSettings(new(utp.Config)), nil
}
type HTTPAuthenticatorRequest struct {
Version string `json:"version"`
Method string `json:"method"`
Path StringList `json:"path"`
Headers map[string]*StringList `json:"headers"`
}
func (this *HTTPAuthenticatorRequest) Build() (*http.RequestConfig, error) {
config := &http.RequestConfig{
Uri: []string{"/"},
Header: []*http.Header{
{
Name: "Host",
Value: []string{"www.baidu.com", "www.bing.com"},
},
{
Name: "User-Agent",
Value: []string{
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46",
},
},
{
Name: "Accept-Encoding",
Value: []string{"gzip, deflate"},
},
{
Name: "Connection",
Value: []string{"keep-alive"},
},
{
Name: "Pragma",
Value: []string{"no-cache"},
},
},
}
if len(this.Version) > 0 {
config.Version = &http.Version{Value: this.Version}
}
if len(this.Method) > 0 {
config.Method = &http.Method{Value: this.Method}
}
if len(this.Path) > 0 {
config.Uri = append([]string(nil), (this.Path)...)
}
if len(this.Headers) > 0 {
config.Header = make([]*http.Header, 0, len(this.Headers))
for key, value := range this.Headers {
if value == nil {
return nil, errors.New("Empty HTTP header value: " + key)
}
config.Header = append(config.Header, &http.Header{
Name: key,
Value: append([]string(nil), (*value)...),
})
}
}
return config, nil
}
type HTTPAuthenticatorResponse struct {
Version string `json:"version"`
Status string `json:"status"`
Reason string `json:"reason"`
Headers map[string]*StringList `json:"headers"`
}
func (this *HTTPAuthenticatorResponse) Build() (*http.ResponseConfig, error) {
config := &http.ResponseConfig{
Header: []*http.Header{
{
Name: "Content-Type",
Value: []string{"application/octet-stream", "video/mpeg"},
},
{
Name: "Transfer-Encoding",
Value: []string{"chunked"},
},
{
Name: "Connection",
Value: []string{"keep-alive"},
},
{
Name: "Pragma",
Value: []string{"no-cache"},
},
},
}
if len(this.Version) > 0 {
config.Version = &http.Version{Value: this.Version}
}
if len(this.Status) > 0 || len(this.Reason) > 0 {
config.Status = &http.Status{
Code: "200",
Reason: "OK",
}
if len(this.Status) > 0 {
config.Status.Code = this.Status
}
if len(this.Reason) > 0 {
config.Status.Reason = this.Reason
}
}
if len(this.Headers) > 0 {
config.Header = make([]*http.Header, 0, len(this.Headers))
for key, value := range this.Headers {
if value == nil {
return nil, errors.New("Empty HTTP header value: " + key)
}
config.Header = append(config.Header, &http.Header{
Name: key,
Value: append([]string(nil), (*value)...),
})
}
}
return config, nil
}
type HTTPAuthenticator struct {
Request HTTPAuthenticatorRequest `json:"request"`
Response HTTPAuthenticatorResponse `json:"response"`
}
func (this *HTTPAuthenticator) Build() (*loader.TypedSettings, error) {
config := new(http.Config)
requestConfig, err := this.Request.Build()
if err != nil {
return nil, err
}
config.Request = requestConfig
responseConfig, err := this.Response.Build()
if err != nil {
return nil, err
}
config.Response = responseConfig
return loader.NewTypedSettings(config), nil
}

View File

@@ -22,6 +22,11 @@ var (
"srtp": func() interface{} { return new(SRTPAuthenticator) },
"utp": func() interface{} { return new(UTPAuthenticator) },
}, "type", "")
tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
"none": func() interface{} { return new(NoOpConnectionAuthenticator) },
"http": func() interface{} { return new(HTTPAuthenticator) },
}, "type", "")
)
type KCPConfig struct {
@@ -93,7 +98,8 @@ func (this *KCPConfig) Build() (*loader.TypedSettings, error) {
}
type TCPConfig struct {
ConnectionReuse *bool `json:"connectionReuse"`
ConnectionReuse *bool `json:"connectionReuse"`
HeaderConfig json.RawMessage `json:"header"`
}
func (this *TCPConfig) Build() (*loader.TypedSettings, error) {
@@ -103,6 +109,18 @@ func (this *TCPConfig) Build() (*loader.TypedSettings, error) {
Enable: *this.ConnectionReuse,
}
}
if len(this.HeaderConfig) > 0 {
headerConfig, _, err := tcpHeaderLoader.Load(this.HeaderConfig)
if err != nil {
return nil, errors.New("TCP|Config: Failed to parse header config: " + err.Error())
}
ts, err := headerConfig.(Buildable).Build()
if err != nil {
return nil, errors.New("Failed to get TCP authenticator config: " + err.Error())
}
config.HeaderSettings = ts
}
return loader.NewTypedSettings(config), nil
}
@@ -154,9 +172,12 @@ func (this *TLSConfig) Build() (*loader.TypedSettings, error) {
}
type StreamConfig struct {
Network *Network `json:"network"`
Security string `json:"security"`
TLSSettings *TLSConfig `json:"tlsSettings"`
Network *Network `json:"network"`
Security string `json:"security"`
TLSSettings *TLSConfig `json:"tlsSettings"`
TCPSettings *TCPConfig `json:"tcpSettings"`
KCPSettings *KCPConfig `json:"kcpSettings"`
WSSettings *WebSocketConfig `json:"wsSettings"`
}
func (this *StreamConfig) Build() (*internet.StreamConfig, error) {
@@ -177,5 +198,35 @@ func (this *StreamConfig) Build() (*internet.StreamConfig, error) {
}
config.SecuritySettings = append(config.SecuritySettings, ts)
}
if this.TCPSettings != nil {
ts, err := this.TCPSettings.Build()
if err != nil {
return nil, errors.New("Failed to build TCP config: " + err.Error())
}
config.NetworkSettings = append(config.NetworkSettings, &internet.NetworkSettings{
Network: v2net.Network_TCP,
Settings: ts,
})
}
if this.KCPSettings != nil {
ts, err := this.KCPSettings.Build()
if err != nil {
return nil, errors.New("Failed to build KCP config: " + err.Error())
}
config.NetworkSettings = append(config.NetworkSettings, &internet.NetworkSettings{
Network: v2net.Network_KCP,
Settings: ts,
})
}
if this.WSSettings != nil {
ts, err := this.WSSettings.Build()
if err != nil {
return nil, errors.New("Failed to build WebSocket config: " + err.Error())
}
config.NetworkSettings = append(config.NetworkSettings, &internet.NetworkSettings{
Network: v2net.Network_WebSocket,
Settings: ts,
})
}
return config, nil
}

View File

@@ -21,9 +21,10 @@ var (
}, "protocol", "settings")
outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"blackhole": func() interface{} { return new(BlackholeConfig) },
"freedom": func() interface{} { return new(FreedomConfig) },
"vmess": func() interface{} { return new(VMessOutboundConfig) },
"blackhole": func() interface{} { return new(BlackholeConfig) },
"freedom": func() interface{} { return new(FreedomConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
"vmess": func() interface{} { return new(VMessOutboundConfig) },
}, "protocol", "settings")
)
@@ -59,7 +60,7 @@ func (this *InboundConnectionConfig) Build() (*core.InboundConnectionConfig, err
jsonConfig, err := inboundConfigLoader.LoadWithID(this.Settings, this.Protocol)
if err != nil {
return nil, err
return nil, errors.New("Failed to load inbound config: " + err.Error())
}
ts, err := jsonConfig.(Buildable).Build()
if err != nil {
@@ -80,7 +81,7 @@ func (this *OutboundConnectionConfig) Build() (*core.OutboundConnectionConfig, e
config := new(core.OutboundConnectionConfig)
rawConfig, err := outboundConfigLoader.LoadWithID(this.Settings, this.Protocol)
if err != nil {
return nil, err
return nil, errors.New("Failed to parse outbound config: " + err.Error())
}
ts, err := rawConfig.(Buildable).Build()
if err != nil {
@@ -181,7 +182,7 @@ func (this *InboundDetourConfig) Build() (*core.InboundConnectionConfig, error)
rawConfig, err := inboundConfigLoader.LoadWithID(this.Settings, this.Protocol)
if err != nil {
return nil, err
return nil, errors.New("Failed to load inbound detour config: " + err.Error())
}
ts, err := rawConfig.(Buildable).Build()
if err != nil {
@@ -221,7 +222,7 @@ func (this *OutboundDetourConfig) Build() (*core.OutboundConnectionConfig, error
rawConfig, err := outboundConfigLoader.LoadWithID(this.Settings, this.Protocol)
if err != nil {
return nil, err
return nil, errors.New("Failed to parse to outbound detour config: " + err.Error())
}
ts, err := rawConfig.(Buildable).Build()
if err != nil {

View File

@@ -5,7 +5,6 @@ import (
"testing"
"v2ray.com/core/common/protocol"
"v2ray.com/core/proxy/vmess"
"v2ray.com/core/proxy/vmess/outbound"
"v2ray.com/core/testing/assert"
. "v2ray.com/core/tools/conf"
@@ -40,6 +39,6 @@ func TestConfigTargetParsing(t *testing.T) {
config := iConfig.(*outbound.Config)
specPB := config.Receiver[0]
spec := protocol.NewServerSpecFromPB(vmess.NewAccount, *specPB)
spec := protocol.NewServerSpecFromPB(*specPB)
assert.Destination(spec.Destination()).EqualsString("tcp:127.0.0.1:80")
}

66
tools/geoip/geoip.pb.go Normal file
View File

@@ -0,0 +1,66 @@
// Code generated by protoc-gen-go.
// source: v2ray.com/core/tools/geoip/geoip.proto
// DO NOT EDIT!
/*
Package geoip is a generated protocol buffer package.
It is generated from these files:
v2ray.com/core/tools/geoip/geoip.proto
It has these top-level messages:
CountryIPRange
*/
package geoip
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import v2ray_core_app_router "v2ray.com/core/app/router"
// 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 CountryIPRange struct {
Ips []*v2ray_core_app_router.CIDR `protobuf:"bytes,1,rep,name=ips" json:"ips,omitempty"`
}
func (m *CountryIPRange) Reset() { *m = CountryIPRange{} }
func (m *CountryIPRange) String() string { return proto.CompactTextString(m) }
func (*CountryIPRange) ProtoMessage() {}
func (*CountryIPRange) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *CountryIPRange) GetIps() []*v2ray_core_app_router.CIDR {
if m != nil {
return m.Ips
}
return nil
}
func init() {
proto.RegisterType((*CountryIPRange)(nil), "v2ray.core.tools.geoip.CountryIPRange")
}
func init() { proto.RegisterFile("v2ray.com/core/tools/geoip/geoip.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 155 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x52, 0x2b, 0x33, 0x2a, 0x4a,
0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x2f, 0xc9, 0xcf, 0xcf, 0x29,
0xd6, 0x4f, 0x4f, 0xcd, 0xcf, 0x2c, 0x80, 0x90, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x62,
0x30, 0x75, 0x45, 0xa9, 0x7a, 0x60, 0x35, 0x7a, 0x60, 0x59, 0x29, 0x74, 0xfd, 0x89, 0x05, 0x05,
0xfa, 0x45, 0xf9, 0xa5, 0x25, 0xa9, 0x45, 0xfa, 0xc9, 0xf9, 0x79, 0x69, 0x99, 0xe9, 0x10, 0xfd,
0x4a, 0xf6, 0x5c, 0x7c, 0xce, 0xf9, 0xa5, 0x79, 0x25, 0x45, 0x95, 0x9e, 0x01, 0x41, 0x89, 0x79,
0xe9, 0xa9, 0x42, 0xba, 0x5c, 0xcc, 0x99, 0x05, 0xc5, 0x12, 0x8c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46,
0xd2, 0x7a, 0x48, 0xe6, 0x27, 0x16, 0x14, 0xe8, 0x41, 0xcc, 0xd0, 0x73, 0xf6, 0x74, 0x09, 0x0a,
0x02, 0xa9, 0x73, 0x62, 0x8f, 0x62, 0x05, 0xdb, 0x98, 0xc4, 0x06, 0x36, 0xd0, 0x18, 0x10, 0x00,
0x00, 0xff, 0xff, 0xf8, 0x02, 0xe8, 0xc0, 0xba, 0x00, 0x00, 0x00,
}

10
tools/geoip/geoip.proto Normal file
View File

@@ -0,0 +1,10 @@
syntax = "proto3";
package v2ray.core.tools.geoip;
option go_package = "geoip";
import "v2ray.com/core/app/router/config.proto";
message CountryIPRange {
repeated v2ray.core.app.router.CIDR ips = 1;
}

File diff suppressed because one or more lines are too long

View File

@@ -12,17 +12,17 @@ import (
"os"
"strconv"
"strings"
"v2ray.com/core/app/router"
"v2ray.com/core/tools/geoip"
"github.com/golang/protobuf/proto"
)
const (
apnicFile = "http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest"
)
type IPEntry struct {
IP []byte
Bits uint32
}
func main() {
resp, err := http.Get(apnicFile)
if err != nil {
@@ -34,7 +34,9 @@ func main() {
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
ips := make([]IPEntry, 0, 8192)
ips := &geoip.CountryIPRange{
Ips: make([]*router.CIDR, 0, 8192),
}
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)
@@ -55,12 +57,17 @@ func main() {
if len(ipBytes) == 0 {
panic("Invalid IP " + ip)
}
ips = append(ips, IPEntry{
IP: []byte(ipBytes),
Bits: mask,
ips.Ips = append(ips.Ips, &router.CIDR{
Ip: []byte(ipBytes)[12:16],
Prefix: 32 - mask,
})
}
ipbytes, err := proto.Marshal(ips)
if err != nil {
log.Fatalf("Failed to marshal country IPs: %v", err)
}
file, err := os.OpenFile("geoip_data.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
log.Fatalf("Failed to generate geoip_data.go: %v", err)
@@ -68,18 +75,8 @@ func main() {
defer file.Close()
fmt.Fprintln(file, "package geoip")
fmt.Fprintln(file, "import \"v2ray.com/core/app/router\"")
fmt.Fprintln(file, "var ChinaIPs []*router.IP")
fmt.Fprintln(file, "func init() {")
fmt.Fprintln(file, "ChinaIPs = []*router.IP {")
for _, ip := range ips {
fmt.Fprintln(file, "&router.IP{", formatArray(ip.IP[12:16]), ",", ip.Bits, "},")
}
fmt.Fprintln(file, "}")
fmt.Fprintln(file, "}")
fmt.Fprintln(file, "var ChinaIPs = "+formatArray(ipbytes))
}
func formatArray(a []byte) string {

View File

@@ -12,5 +12,5 @@ $GOPATH/bin/build --os=linux --arch=arm64 --zip
$GOPATH/bin/build --os=linux --arch=mips64 --zip
$GOPATH/bin/build --os=freebsd --arch=x86 --zip
$GOPATH/bin/build --os=freebsd --arch=amd64 --zip
$GOPATH/bin/build --os=openbsd --arch=amd64 --zip
$GOPATH/bin/build --os=openbsd --arch=x86 --zip
$GOPATH/bin/build --os=openbsd --arch=amd64 --zip

View File

@@ -29,6 +29,7 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Global transport settings. This affects all type of connections that go through V2Ray.
type Config struct {
NetworkSettings []*v2ray_core_transport_internet.NetworkSettings `protobuf:"bytes,1,rep,name=network_settings,json=networkSettings" json:"network_settings,omitempty"`
}

View File

@@ -7,6 +7,7 @@ option java_outer_classname = "ConfigProto";
import "v2ray.com/core/transport/internet/config.proto";
// Global transport settings. This affects all type of connections that go through V2Ray.
message Config {
repeated v2ray.core.transport.internet.NetworkSettings network_settings = 1;
}

View File

@@ -3,7 +3,6 @@ package internet
import (
"v2ray.com/core/common"
"v2ray.com/core/common/alloc"
"v2ray.com/core/common/loader"
)
type Authenticator interface {
@@ -18,7 +17,6 @@ type AuthenticatorFactory interface {
var (
authenticatorCache = make(map[string]AuthenticatorFactory)
configCache = loader.ConfigCreatorCache{}
)
func RegisterAuthenticator(name string, factory AuthenticatorFactory) error {

View File

@@ -1,75 +0,0 @@
// Code generated by protoc-gen-go.
// source: v2ray.com/core/transport/internet/authenticator.proto
// DO NOT EDIT!
/*
Package internet is a generated protocol buffer package.
It is generated from these files:
v2ray.com/core/transport/internet/authenticator.proto
v2ray.com/core/transport/internet/config.proto
It has these top-level messages:
AuthenticatorConfig
NetworkSettings
StreamConfig
*/
package internet
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import v2ray_core_common_loader "v2ray.com/core/common/loader"
// 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 AuthenticatorConfig struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Settings *v2ray_core_common_loader.TypedSettings `protobuf:"bytes,2,opt,name=settings" json:"settings,omitempty"`
}
func (m *AuthenticatorConfig) Reset() { *m = AuthenticatorConfig{} }
func (m *AuthenticatorConfig) String() string { return proto.CompactTextString(m) }
func (*AuthenticatorConfig) ProtoMessage() {}
func (*AuthenticatorConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *AuthenticatorConfig) GetSettings() *v2ray_core_common_loader.TypedSettings {
if m != nil {
return m.Settings
}
return nil
}
func init() {
proto.RegisterType((*AuthenticatorConfig)(nil), "v2ray.core.transport.internet.AuthenticatorConfig")
}
func init() {
proto.RegisterFile("v2ray.com/core/transport/internet/authenticator.proto", fileDescriptor0)
}
var fileDescriptor0 = []byte{
// 207 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x8e, 0xc1, 0x4a, 0xc4, 0x30,
0x10, 0x86, 0xa9, 0x88, 0xac, 0xf1, 0x16, 0x2f, 0x8b, 0x20, 0xac, 0x5e, 0x76, 0x4f, 0x13, 0x58,
0xf1, 0x01, 0xda, 0xbe, 0x80, 0x54, 0x4f, 0xde, 0x62, 0x3a, 0xd6, 0x80, 0x99, 0x09, 0xd3, 0x51,
0xe8, 0xdb, 0x8b, 0xad, 0x2d, 0xd5, 0x83, 0xb7, 0x40, 0xe6, 0xfb, 0xfe, 0xcf, 0xdc, 0x7f, 0x1e,
0xc5, 0x0f, 0x10, 0x38, 0xb9, 0xc0, 0x82, 0x4e, 0xc5, 0x53, 0x9f, 0x59, 0xd4, 0x45, 0x52, 0x14,
0x42, 0x75, 0xfe, 0x43, 0xdf, 0x90, 0x34, 0x06, 0xaf, 0x2c, 0x90, 0x85, 0x95, 0xed, 0xf5, 0x8c,
0x09, 0xc2, 0x82, 0xc0, 0x8c, 0x5c, 0xed, 0xff, 0x58, 0x03, 0xa7, 0xc4, 0xe4, 0xde, 0xd9, 0xb7,
0x28, 0x4e, 0x87, 0x8c, 0x93, 0xe7, 0x96, 0xcc, 0x65, 0xb9, 0xd6, 0xd7, 0x4c, 0xaf, 0xb1, 0xb3,
0xd6, 0x9c, 0x92, 0x4f, 0xb8, 0x2d, 0x76, 0xc5, 0xe1, 0xbc, 0x19, 0xdf, 0xb6, 0x36, 0x9b, 0x1e,
0x55, 0x23, 0x75, 0xfd, 0xf6, 0x64, 0x57, 0x1c, 0x2e, 0x8e, 0x7b, 0x58, 0x55, 0x4c, 0x13, 0x30,
0x4d, 0xc0, 0xd3, 0x90, 0xb1, 0x7d, 0xfc, 0x39, 0x6f, 0x16, 0xb0, 0x2a, 0xcd, 0x4d, 0xe0, 0x04,
0xff, 0xd6, 0x57, 0xf6, 0x57, 0xd2, 0xc3, 0x77, 0xe8, 0xf3, 0x66, 0xfe, 0x7d, 0x39, 0x1b, 0xcb,
0xef, 0xbe, 0x02, 0x00, 0x00, 0xff, 0xff, 0x86, 0xf6, 0xf6, 0xdf, 0x3a, 0x01, 0x00, 0x00,
}

View File

@@ -1,13 +0,0 @@
syntax = "proto3";
package v2ray.core.transport.internet;
option go_package = "internet";
option java_package = "com.v2ray.core.transport.internet";
option java_outer_classname = "AuthenticatorProto";
import "v2ray.com/core/common/loader/type.proto";
message AuthenticatorConfig {
string name = 1;
v2ray.core.common.loader.TypedSettings settings = 2;
}

View File

@@ -0,0 +1,96 @@
package http
import (
"strings"
"v2ray.com/core/common/dice"
)
func (this *Version) GetValue() string {
if this == nil {
return "1.1"
}
return this.Value
}
func (this *Method) GetValue() string {
if this == nil {
return "GET"
}
return this.Value
}
func (this *Status) GetCode() string {
if this == nil {
return "200"
}
return this.Code
}
func (this *Status) GetReason() string {
if this == nil {
return "OK"
}
return this.Reason
}
func pickString(arr []string) string {
n := len(arr)
switch n {
case 0:
return ""
case 1:
return arr[0]
default:
return arr[dice.Roll(n)]
}
}
func (this *RequestConfig) PickUri() string {
return pickString(this.Uri)
}
func (this *RequestConfig) PickHeaders() []string {
n := len(this.Header)
if n == 0 {
return nil
}
headers := make([]string, n)
for idx, headerConfig := range this.Header {
headerName := headerConfig.Name
headerValue := pickString(headerConfig.Value)
headers[idx] = headerName + ": " + headerValue
}
return headers
}
func (this *RequestConfig) GetFullVersion() string {
return "HTTP/" + this.Version.GetValue()
}
func (this *ResponseConfig) HasHeader(header string) bool {
cHeader := strings.ToLower(header)
for _, tHeader := range this.Header {
if strings.ToLower(tHeader.Name) == cHeader {
return true
}
}
return false
}
func (this *ResponseConfig) PickHeaders() []string {
n := len(this.Header)
if n == 0 {
return nil
}
headers := make([]string, n)
for idx, headerConfig := range this.Header {
headerName := headerConfig.Name
headerValue := pickString(headerConfig.Value)
headers[idx] = headerName + ": " + headerValue
}
return headers
}
func (this *ResponseConfig) GetFullVersion() string {
return "HTTP/" + this.Version.GetValue()
}

View File

@@ -0,0 +1,216 @@
// Code generated by protoc-gen-go.
// source: v2ray.com/core/transport/internet/authenticators/http/config.proto
// DO NOT EDIT!
/*
Package http is a generated protocol buffer package.
It is generated from these files:
v2ray.com/core/transport/internet/authenticators/http/config.proto
It has these top-level messages:
Header
Version
Method
RequestConfig
Status
ResponseConfig
Config
*/
package http
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 Header struct {
// "Accept", "Cookie", etc
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// Each entry must be valid in one piece. Random entry will be chosen if multiple entries present.
Value []string `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"`
}
func (m *Header) Reset() { *m = Header{} }
func (m *Header) String() string { return proto.CompactTextString(m) }
func (*Header) ProtoMessage() {}
func (*Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// HTTP version. Default value "1.1".
type Version struct {
Value string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
}
func (m *Version) Reset() { *m = Version{} }
func (m *Version) String() string { return proto.CompactTextString(m) }
func (*Version) ProtoMessage() {}
func (*Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
// HTTP method. Default value "GET".
type Method struct {
Value string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
}
func (m *Method) Reset() { *m = Method{} }
func (m *Method) String() string { return proto.CompactTextString(m) }
func (*Method) ProtoMessage() {}
func (*Method) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
type RequestConfig struct {
// Full HTTP version like "1.1".
Version *Version `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"`
// GET, POST, CONNECT etc
Method *Method `protobuf:"bytes,2,opt,name=method" json:"method,omitempty"`
// URI like "/login.php"
Uri []string `protobuf:"bytes,3,rep,name=uri" json:"uri,omitempty"`
Header []*Header `protobuf:"bytes,4,rep,name=header" json:"header,omitempty"`
}
func (m *RequestConfig) Reset() { *m = RequestConfig{} }
func (m *RequestConfig) String() string { return proto.CompactTextString(m) }
func (*RequestConfig) ProtoMessage() {}
func (*RequestConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *RequestConfig) GetVersion() *Version {
if m != nil {
return m.Version
}
return nil
}
func (m *RequestConfig) GetMethod() *Method {
if m != nil {
return m.Method
}
return nil
}
func (m *RequestConfig) GetHeader() []*Header {
if m != nil {
return m.Header
}
return nil
}
type Status struct {
// Status code. Default "200".
Code string `protobuf:"bytes,1,opt,name=code" json:"code,omitempty"`
// Statue reason. Default "OK".
Reason string `protobuf:"bytes,2,opt,name=reason" json:"reason,omitempty"`
}
func (m *Status) Reset() { *m = Status{} }
func (m *Status) String() string { return proto.CompactTextString(m) }
func (*Status) ProtoMessage() {}
func (*Status) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
type ResponseConfig struct {
Version *Version `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"`
Status *Status `protobuf:"bytes,2,opt,name=status" json:"status,omitempty"`
Header []*Header `protobuf:"bytes,3,rep,name=header" json:"header,omitempty"`
}
func (m *ResponseConfig) Reset() { *m = ResponseConfig{} }
func (m *ResponseConfig) String() string { return proto.CompactTextString(m) }
func (*ResponseConfig) ProtoMessage() {}
func (*ResponseConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *ResponseConfig) GetVersion() *Version {
if m != nil {
return m.Version
}
return nil
}
func (m *ResponseConfig) GetStatus() *Status {
if m != nil {
return m.Status
}
return nil
}
func (m *ResponseConfig) GetHeader() []*Header {
if m != nil {
return m.Header
}
return nil
}
type Config struct {
// Settings for authenticating requests. If not set, client side will not send authenication header, and server side will bypass authentication.
Request *RequestConfig `protobuf:"bytes,1,opt,name=request" json:"request,omitempty"`
// Settings for authenticating responses. If not set, client side will bypass authentication, and server side will not send authentication header.
Response *ResponseConfig `protobuf:"bytes,2,opt,name=response" json:"response,omitempty"`
}
func (m *Config) Reset() { *m = Config{} }
func (m *Config) String() string { return proto.CompactTextString(m) }
func (*Config) ProtoMessage() {}
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *Config) GetRequest() *RequestConfig {
if m != nil {
return m.Request
}
return nil
}
func (m *Config) GetResponse() *ResponseConfig {
if m != nil {
return m.Response
}
return nil
}
func init() {
proto.RegisterType((*Header)(nil), "v2ray.core.transport.internet.authenticators.http.Header")
proto.RegisterType((*Version)(nil), "v2ray.core.transport.internet.authenticators.http.Version")
proto.RegisterType((*Method)(nil), "v2ray.core.transport.internet.authenticators.http.Method")
proto.RegisterType((*RequestConfig)(nil), "v2ray.core.transport.internet.authenticators.http.RequestConfig")
proto.RegisterType((*Status)(nil), "v2ray.core.transport.internet.authenticators.http.Status")
proto.RegisterType((*ResponseConfig)(nil), "v2ray.core.transport.internet.authenticators.http.ResponseConfig")
proto.RegisterType((*Config)(nil), "v2ray.core.transport.internet.authenticators.http.Config")
}
func init() {
proto.RegisterFile("v2ray.com/core/transport/internet/authenticators/http/config.proto", fileDescriptor0)
}
var fileDescriptor0 = []byte{
// 389 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x94, 0xc1, 0x4e, 0xe3, 0x30,
0x10, 0x40, 0xd5, 0xa4, 0x9b, 0x6e, 0xa7, 0xda, 0xd5, 0xca, 0x5a, 0xad, 0x72, 0xda, 0xad, 0x72,
0xea, 0xc9, 0xd1, 0x76, 0x97, 0x03, 0x9c, 0xa0, 0x5c, 0x10, 0x12, 0x12, 0x18, 0xc4, 0xa1, 0x12,
0x07, 0x93, 0x0e, 0x24, 0x12, 0xb1, 0x83, 0xed, 0x54, 0xe2, 0x1f, 0xf8, 0x05, 0xbe, 0x86, 0x1f,
0x43, 0xb1, 0x9d, 0xd2, 0x1e, 0x38, 0x34, 0xc0, 0xcd, 0x4e, 0x66, 0x9e, 0x67, 0xde, 0x58, 0x86,
0xd9, 0x72, 0xaa, 0xf8, 0x03, 0xcd, 0x64, 0x99, 0x66, 0x52, 0x61, 0x6a, 0x14, 0x17, 0xba, 0x92,
0xca, 0xa4, 0x85, 0x30, 0xa8, 0x04, 0x9a, 0x94, 0xd7, 0x26, 0x47, 0x61, 0x8a, 0x8c, 0x1b, 0xa9,
0x74, 0x9a, 0x1b, 0x53, 0xa5, 0x99, 0x14, 0x37, 0xc5, 0x2d, 0xad, 0x94, 0x34, 0x92, 0xfc, 0x6d,
0x19, 0x0a, 0xe9, 0x2a, 0x9f, 0xb6, 0xf9, 0x74, 0x33, 0x9f, 0x36, 0xf9, 0xc9, 0x14, 0xa2, 0x23,
0xe4, 0x0b, 0x54, 0x84, 0x40, 0x5f, 0xf0, 0x12, 0xe3, 0xde, 0xb8, 0x37, 0x19, 0x32, 0xbb, 0x26,
0x3f, 0xe1, 0xcb, 0x92, 0xdf, 0xd5, 0x18, 0x07, 0xe3, 0x70, 0x32, 0x64, 0x6e, 0x93, 0xfc, 0x81,
0xc1, 0x25, 0x2a, 0x5d, 0x48, 0xf1, 0x1a, 0xe0, 0xb2, 0x7c, 0xc0, 0x6f, 0x88, 0x4e, 0xd0, 0xe4,
0x72, 0xf1, 0xc6, 0xff, 0xa7, 0x00, 0xbe, 0x31, 0xbc, 0xaf, 0x51, 0x9b, 0x43, 0x5b, 0x3f, 0xb9,
0x80, 0xc1, 0xd2, 0x21, 0x6d, 0xe4, 0x68, 0xba, 0x47, 0xb7, 0xee, 0x85, 0xfa, 0xa2, 0x58, 0x8b,
0x22, 0x67, 0x10, 0x95, 0xb6, 0x8e, 0x38, 0xb0, 0xd0, 0xdd, 0x0e, 0x50, 0xd7, 0x08, 0xf3, 0x20,
0xf2, 0x03, 0xc2, 0x5a, 0x15, 0x71, 0x68, 0x7d, 0x34, 0xcb, 0xe6, 0x90, 0xdc, 0x1a, 0x8c, 0xfb,
0xe3, 0xb0, 0xe3, 0x21, 0x6e, 0x04, 0xcc, 0x83, 0x92, 0xff, 0x10, 0x9d, 0x1b, 0x6e, 0x6a, 0xdd,
0x0c, 0x25, 0x93, 0x8b, 0xd5, 0x50, 0x9a, 0x35, 0xf9, 0x05, 0x91, 0x42, 0xae, 0xa5, 0xb0, 0x5d,
0x0d, 0x99, 0xdf, 0x25, 0x8f, 0x01, 0x7c, 0x67, 0xa8, 0x2b, 0x29, 0x34, 0x7e, 0xb6, 0x56, 0x6d,
0xcb, 0x7b, 0x87, 0x56, 0xd7, 0x1f, 0xf3, 0xa0, 0x35, 0x89, 0xe1, 0x47, 0x49, 0x7c, 0xee, 0x41,
0xe4, 0x35, 0xcc, 0x61, 0xa0, 0xdc, 0x75, 0xf3, 0x1a, 0xf6, 0x3b, 0xe0, 0x37, 0x2e, 0x2c, 0x6b,
0x81, 0xe4, 0x0a, 0xbe, 0x2a, 0x2f, 0xdd, 0xeb, 0x38, 0xe8, 0x04, 0x5f, 0x9f, 0x1b, 0x5b, 0x21,
0x67, 0xc7, 0xb0, 0x93, 0xc9, 0x72, 0x7b, 0xe2, 0x6c, 0xe4, 0x50, 0xa7, 0xcd, 0xc3, 0x30, 0xef,
0x37, 0x9f, 0xae, 0x23, 0xfb, 0x4a, 0xfc, 0x7b, 0x09, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x1d, 0x02,
0x42, 0x6b, 0x04, 0x00, 0x00,
}

View File

@@ -0,0 +1,61 @@
syntax = "proto3";
package v2ray.core.transport.internet.authenticators.http;
option go_package = "http";
option java_package = "com.v2ray.core.transport.internet.authenticators.http";
option java_outer_classname = "ConfigProto";
message Header {
// "Accept", "Cookie", etc
string name = 1;
// Each entry must be valid in one piece. Random entry will be chosen if multiple entries present.
repeated string value = 2;
}
// HTTP version. Default value "1.1".
message Version {
string value = 1;
}
// HTTP method. Default value "GET".
message Method {
string value = 1;
}
message RequestConfig {
// Full HTTP version like "1.1".
Version version = 1;
// GET, POST, CONNECT etc
Method method = 2;
// URI like "/login.php"
repeated string uri = 3;
repeated Header header = 4;
}
message Status {
// Status code. Default "200".
string code = 1;
// Statue reason. Default "OK".
string reason = 2;
}
message ResponseConfig {
Version version = 1;
Status status = 2;
repeated Header header = 3;
}
message Config {
// Settings for authenticating requests. If not set, client side will not send authenication header, and server side will bypass authentication.
RequestConfig request = 1;
// Settings for authenticating responses. If not set, client side will bypass authentication, and server side will not send authentication header.
ResponseConfig response = 2;
}

View File

@@ -0,0 +1,206 @@
package http
import (
"bytes"
"io"
"net"
"net/http"
"time"
"v2ray.com/core/common/alloc"
"v2ray.com/core/common/loader"
"v2ray.com/core/transport/internet"
)
const (
CRLF = "\r\n"
ENDING = CRLF + CRLF
)
type Reader interface {
Read(io.Reader) (*alloc.Buffer, error)
}
type Writer interface {
Write(io.Writer) error
}
type NoOpReader struct{}
func (this *NoOpReader) Read(io.Reader) (*alloc.Buffer, error) {
return nil, nil
}
type NoOpWriter struct{}
func (this *NoOpWriter) Write(io.Writer) error {
return nil
}
type HeaderReader struct {
}
func (*HeaderReader) Read(reader io.Reader) (*alloc.Buffer, error) {
buffer := alloc.NewLocalBuffer(2048)
for {
_, err := buffer.FillFrom(reader)
if err != nil {
return nil, err
}
if n := bytes.Index(buffer.Value, []byte(ENDING)); n != -1 {
buffer.SliceFrom(n + len(ENDING))
break
}
if buffer.Len() >= len(ENDING) {
copy(buffer.Value, buffer.Value[buffer.Len()-len(ENDING):])
buffer.Slice(0, len(ENDING))
}
}
if buffer.IsEmpty() {
buffer.Release()
return nil, nil
}
return buffer, nil
}
type HeaderWriter struct {
header *alloc.Buffer
}
func NewHeaderWriter(header *alloc.Buffer) *HeaderWriter {
return &HeaderWriter{
header: header,
}
}
func (this *HeaderWriter) Write(writer io.Writer) error {
if this.header == nil {
return nil
}
_, err := writer.Write(this.header.Value)
this.header.Release()
this.header = nil
return err
}
type HttpConn struct {
net.Conn
readBuffer *alloc.Buffer
oneTimeReader Reader
oneTimeWriter Writer
}
func NewHttpConn(conn net.Conn, reader Reader, writer Writer) *HttpConn {
return &HttpConn{
Conn: conn,
oneTimeReader: reader,
oneTimeWriter: writer,
}
}
func (this *HttpConn) Read(b []byte) (int, error) {
if this.oneTimeReader != nil {
buffer, err := this.oneTimeReader.Read(this.Conn)
if err != nil {
return 0, err
}
this.readBuffer = buffer
this.oneTimeReader = nil
}
if this.readBuffer.Len() > 0 {
nBytes, err := this.readBuffer.Read(b)
if nBytes == this.readBuffer.Len() {
this.readBuffer.Release()
this.readBuffer = nil
}
return nBytes, err
}
return this.Conn.Read(b)
}
func (this *HttpConn) Write(b []byte) (int, error) {
if this.oneTimeWriter != nil {
err := this.oneTimeWriter.Write(this.Conn)
this.oneTimeWriter = nil
if err != nil {
return 0, err
}
}
return this.Conn.Write(b)
}
type HttpAuthenticator struct {
config *Config
}
func (this HttpAuthenticator) GetClientWriter() *HeaderWriter {
header := alloc.NewLocalBuffer(2048).Clear()
config := this.config.Request
header.AppendString(config.Method.GetValue()).AppendString(" ").AppendString(config.PickUri()).AppendString(" ").AppendString(config.GetFullVersion()).AppendString(CRLF)
headers := config.PickHeaders()
for _, h := range headers {
header.AppendString(h).AppendString(CRLF)
}
header.AppendString(CRLF)
return &HeaderWriter{
header: header,
}
}
func (this HttpAuthenticator) GetServerWriter() *HeaderWriter {
header := alloc.NewLocalBuffer(2048).Clear()
config := this.config.Response
header.AppendString(config.GetFullVersion()).AppendString(" ").AppendString(config.Status.GetCode()).AppendString(" ").AppendString(config.Status.GetReason()).AppendString(CRLF)
headers := config.PickHeaders()
for _, h := range headers {
header.AppendString(h).AppendString(CRLF)
}
if !config.HasHeader("Date") {
header.AppendString("Date: ").AppendString(time.Now().Format(http.TimeFormat)).AppendString(CRLF)
}
header.AppendString(CRLF)
return &HeaderWriter{
header: header,
}
}
func (this HttpAuthenticator) Client(conn net.Conn) net.Conn {
if this.config.Request == nil && this.config.Response == nil {
return conn
}
var reader Reader = new(NoOpReader)
if this.config.Request != nil {
reader = new(HeaderReader)
}
var writer Writer = new(NoOpWriter)
if this.config.Response != nil {
writer = this.GetClientWriter()
}
return NewHttpConn(conn, reader, writer)
}
func (this HttpAuthenticator) Server(conn net.Conn) net.Conn {
if this.config.Request == nil && this.config.Response == nil {
return conn
}
return NewHttpConn(conn, new(HeaderReader), this.GetServerWriter())
}
type HttpAuthenticatorFactory struct{}
func (HttpAuthenticatorFactory) Create(config interface{}) internet.ConnectionAuthenticator {
return HttpAuthenticator{
config: config.(*Config),
}
}
func init() {
internet.RegisterConnectionAuthenticator(loader.GetType(new(Config)), HttpAuthenticatorFactory{})
}

View File

@@ -0,0 +1,46 @@
package http_test
import (
"testing"
"v2ray.com/core/common/alloc"
"v2ray.com/core/testing/assert"
. "v2ray.com/core/transport/internet/authenticators/http"
)
func TestReaderWriter(t *testing.T) {
assert := assert.On(t)
cache := alloc.NewBuffer()
writer := NewHeaderWriter(alloc.NewLocalBuffer(256).Clear().AppendString("abcd" + ENDING))
writer.Write(cache)
cache.Write([]byte{'e', 'f', 'g'})
reader := &HeaderReader{}
buffer, err := reader.Read(cache)
assert.Error(err).IsNil()
assert.Bytes(buffer.Value).Equals([]byte{'e', 'f', 'g'})
}
func TestRequestHeader(t *testing.T) {
assert := assert.On(t)
factory := HttpAuthenticatorFactory{}
auth := factory.Create(&Config{
Request: &RequestConfig{
Uri: []string{"/"},
Header: []*Header{
{
Name: "Test",
Value: []string{"Value"},
},
},
},
}).(HttpAuthenticator)
cache := alloc.NewBuffer().Clear()
err := auth.GetClientWriter().Write(cache)
assert.Error(err).IsNil()
assert.String(cache.String()).Equals("GET / HTTP/1.1\r\nTest: Value\r\n\r\n")
}

View File

@@ -1,6 +1,8 @@
package noop
import (
"net"
"v2ray.com/core/common/alloc"
"v2ray.com/core/common/loader"
"v2ray.com/core/transport/internet"
@@ -22,6 +24,23 @@ func (this NoOpAuthenticatorFactory) Create(config interface{}) internet.Authent
return NoOpAuthenticator{}
}
type NoOpConnectionAuthenticator struct{}
func (NoOpConnectionAuthenticator) Client(conn net.Conn) net.Conn {
return conn
}
func (NoOpConnectionAuthenticator) Server(conn net.Conn) net.Conn {
return conn
}
type NoOpConnectionAuthenticatorFactory struct{}
func (NoOpConnectionAuthenticatorFactory) Create(config interface{}) internet.ConnectionAuthenticator {
return NoOpConnectionAuthenticator{}
}
func init() {
internet.RegisterAuthenticator(loader.GetType(new(Config)), NoOpAuthenticatorFactory{})
internet.RegisterConnectionAuthenticator(loader.GetType(new(Config)), NoOpConnectionAuthenticatorFactory{})
}

View File

@@ -8,15 +8,17 @@ import (
v2net "v2ray.com/core/common/net"
)
type ConfigCreator func() interface{}
var (
globalNetworkConfigCreatorCache = make(map[v2net.Network]loader.ConfigCreator)
globalNetworkConfigCreatorCache = make(map[v2net.Network]ConfigCreator)
globalNetworkSettings []*NetworkSettings
ErrUnconfiguredNetwork = errors.New("Network config creator not set.")
)
func RegisterNetworkConfigCreator(network v2net.Network, creator loader.ConfigCreator) error {
func RegisterNetworkConfigCreator(network v2net.Network, creator ConfigCreator) error {
// TODO: check duplicate
globalNetworkConfigCreatorCache[network] = creator
return nil

View File

@@ -2,6 +2,16 @@
// source: v2ray.com/core/transport/internet/config.proto
// DO NOT EDIT!
/*
Package internet is a generated protocol buffer package.
It is generated from these files:
v2ray.com/core/transport/internet/config.proto
It has these top-level messages:
NetworkSettings
StreamConfig
*/
package internet
import proto "github.com/golang/protobuf/proto"
@@ -15,15 +25,23 @@ 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 NetworkSettings struct {
Network v2ray_core_common_net.Network `protobuf:"varint,1,opt,name=network,enum=v2ray.core.common.net.Network" json:"network,omitempty"`
// Type of network that this settings supports.
Network v2ray_core_common_net.Network `protobuf:"varint,1,opt,name=network,enum=v2ray.core.common.net.Network" json:"network,omitempty"`
// Specific settings.
Settings *v2ray_core_common_loader.TypedSettings `protobuf:"bytes,2,opt,name=settings" json:"settings,omitempty"`
}
func (m *NetworkSettings) Reset() { *m = NetworkSettings{} }
func (m *NetworkSettings) String() string { return proto.CompactTextString(m) }
func (*NetworkSettings) ProtoMessage() {}
func (*NetworkSettings) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
func (*NetworkSettings) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *NetworkSettings) GetSettings() *v2ray_core_common_loader.TypedSettings {
if m != nil {
@@ -33,8 +51,10 @@ func (m *NetworkSettings) GetSettings() *v2ray_core_common_loader.TypedSettings
}
type StreamConfig struct {
Network v2ray_core_common_net.Network `protobuf:"varint,1,opt,name=network,enum=v2ray.core.common.net.Network" json:"network,omitempty"`
NetworkSettings []*NetworkSettings `protobuf:"bytes,2,rep,name=network_settings,json=networkSettings" json:"network_settings,omitempty"`
// Effective network.
Network v2ray_core_common_net.Network `protobuf:"varint,1,opt,name=network,enum=v2ray.core.common.net.Network" json:"network,omitempty"`
NetworkSettings []*NetworkSettings `protobuf:"bytes,2,rep,name=network_settings,json=networkSettings" json:"network_settings,omitempty"`
// Type of security. Must be a message name of the settings proto.
SecurityType string `protobuf:"bytes,3,opt,name=security_type,json=securityType" json:"security_type,omitempty"`
SecuritySettings []*v2ray_core_common_loader.TypedSettings `protobuf:"bytes,4,rep,name=security_settings,json=securitySettings" json:"security_settings,omitempty"`
}
@@ -42,7 +62,7 @@ type StreamConfig struct {
func (m *StreamConfig) Reset() { *m = StreamConfig{} }
func (m *StreamConfig) String() string { return proto.CompactTextString(m) }
func (*StreamConfig) ProtoMessage() {}
func (*StreamConfig) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} }
func (*StreamConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *StreamConfig) GetNetworkSettings() []*NetworkSettings {
if m != nil {
@@ -63,9 +83,9 @@ func init() {
proto.RegisterType((*StreamConfig)(nil), "v2ray.core.transport.internet.StreamConfig")
}
func init() { proto.RegisterFile("v2ray.com/core/transport/internet/config.proto", fileDescriptor1) }
func init() { proto.RegisterFile("v2ray.com/core/transport/internet/config.proto", fileDescriptor0) }
var fileDescriptor1 = []byte{
var fileDescriptor0 = []byte{
// 296 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x91, 0x4f, 0x4b, 0xc3, 0x40,
0x10, 0xc5, 0x49, 0x2b, 0x5a, 0xb7, 0xd5, 0xd6, 0x9c, 0x82, 0xa0, 0xc4, 0x7a, 0x68, 0x2e, 0xce,

View File

@@ -8,13 +8,21 @@ import "v2ray.com/core/common/net/network.proto";
import "v2ray.com/core/common/loader/type.proto";
message NetworkSettings {
// Type of network that this settings supports.
v2ray.core.common.net.Network network = 1;
// Specific settings.
v2ray.core.common.loader.TypedSettings settings = 2;
}
message StreamConfig {
// Effective network.
v2ray.core.common.net.Network network = 1;
repeated NetworkSettings network_settings = 2;
// Type of security. Must be a message name of the settings proto.
string security_type = 3;
repeated v2ray.core.common.loader.TypedSettings security_settings = 4;
}

View File

@@ -0,0 +1,36 @@
package internet
import (
"net"
"v2ray.com/core/common"
)
type ConnectionAuthenticator interface {
Client(net.Conn) net.Conn
Server(net.Conn) net.Conn
}
type ConnectionAuthenticatorFactory interface {
Create(interface{}) ConnectionAuthenticator
}
var (
connectionAuthenticatorCache = make(map[string]ConnectionAuthenticatorFactory)
)
func RegisterConnectionAuthenticator(name string, factory ConnectionAuthenticatorFactory) error {
if _, found := connectionAuthenticatorCache[name]; found {
return common.ErrDuplicatedName
}
connectionAuthenticatorCache[name] = factory
return nil
}
func CreateConnectionAuthenticator(name string, config interface{}) (ConnectionAuthenticator, error) {
factory, found := connectionAuthenticatorCache[name]
if !found {
return nil, common.ErrObjectNotFound
}
return factory.Create(config), nil
}

View File

@@ -12,7 +12,8 @@ var (
)
type DialerOptions struct {
Stream *StreamConfig
Stream *StreamConfig
ProxyTag string
}
type Dialer func(src v2net.Address, dest v2net.Destination, options DialerOptions) (Connection, error)
@@ -23,27 +24,28 @@ var (
RawTCPDialer Dialer
UDPDialer Dialer
WSDialer Dialer
ProxyDialer Dialer
)
func Dial(src v2net.Address, dest v2net.Destination, settings *StreamConfig) (Connection, error) {
func Dial(src v2net.Address, dest v2net.Destination, options DialerOptions) (Connection, error) {
if len(options.ProxyTag) > 0 && ProxyDialer != nil {
return ProxyDialer(src, dest, options)
}
var connection Connection
var err error
dialerOptions := DialerOptions{
Stream: settings,
}
if dest.Network == v2net.Network_TCP {
switch settings.Network {
switch options.Stream.Network {
case v2net.Network_TCP:
connection, err = TCPDialer(src, dest, dialerOptions)
connection, err = TCPDialer(src, dest, options)
case v2net.Network_KCP:
connection, err = KCPDialer(src, dest, dialerOptions)
connection, err = KCPDialer(src, dest, options)
case v2net.Network_WebSocket:
connection, err = WSDialer(src, dest, dialerOptions)
connection, err = WSDialer(src, dest, options)
// This check has to be the last one.
case v2net.Network_RawTCP:
connection, err = RawTCPDialer(src, dest, dialerOptions)
connection, err = RawTCPDialer(src, dest, options)
default:
return nil, ErrUnsupportedStreamType
}
@@ -54,7 +56,7 @@ func Dial(src v2net.Address, dest v2net.Destination, settings *StreamConfig) (Co
return connection, nil
}
return UDPDialer(src, dest, dialerOptions)
return UDPDialer(src, dest, options)
}
func DialToDest(src v2net.Address, dest v2net.Destination) (net.Conn, error) {

View File

@@ -1,102 +0,0 @@
package kcp
import (
"sync"
"v2ray.com/core/common/alloc"
)
const (
NumDistro = 5
DistroSize = 1600
)
type Buffer struct {
sync.Mutex
buffer *alloc.Buffer
next int
released int
hold bool
distro [NumDistro]*alloc.Buffer
}
func NewBuffer() *Buffer {
b := &Buffer{
next: 0,
released: 0,
hold: true,
buffer: alloc.NewBuffer(),
}
for idx := range b.distro {
content := b.buffer.Value[idx*DistroSize : (idx+1)*DistroSize]
b.distro[idx] = alloc.CreateBuffer(content, b)
}
return b
}
func (this *Buffer) IsEmpty() bool {
this.Lock()
defer this.Unlock()
return this.next == NumDistro
}
func (this *Buffer) Allocate() *alloc.Buffer {
this.Lock()
defer this.Unlock()
if this.next == NumDistro {
return nil
}
b := this.distro[this.next]
this.next++
return b
}
func (this *Buffer) Free(b *alloc.Buffer) {
this.Lock()
defer this.Unlock()
this.released++
if !this.hold && this.released == this.next {
this.ReleaseBuffer()
}
}
func (this *Buffer) Release() {
this.Lock()
defer this.Unlock()
if this.next == this.released {
this.ReleaseBuffer()
}
this.hold = false
}
func (this *Buffer) ReleaseBuffer() {
this.buffer.Release()
this.buffer = nil
for idx := range this.distro {
this.distro[idx] = nil
}
}
var (
globalBuffer *Buffer
globalBufferAccess sync.Mutex
)
func AllocateBuffer() *alloc.Buffer {
globalBufferAccess.Lock()
defer globalBufferAccess.Unlock()
if globalBuffer == nil {
globalBuffer = NewBuffer()
}
b := globalBuffer.Allocate()
if globalBuffer.IsEmpty() {
globalBuffer.Release()
globalBuffer = nil
}
return b
}

View File

@@ -1,39 +0,0 @@
package kcp_test
import (
"testing"
"v2ray.com/core/testing/assert"
. "v2ray.com/core/transport/internet/kcp"
)
func TestBuffer(t *testing.T) {
assert := assert.On(t)
b := NewBuffer()
for i := 0; i < NumDistro; i++ {
x := b.Allocate()
assert.Pointer(x).IsNotNil()
x.Release()
}
assert.Pointer(b.Allocate()).IsNil()
b.Release()
}
func TestSingleRelease(t *testing.T) {
assert := assert.On(t)
b := NewBuffer()
x := b.Allocate()
x.Release()
y := b.Allocate()
assert.Pointer(y.Value).IsNotNil()
b.Release()
y.Release()
z := b.Allocate()
assert.Pointer(z).IsNil()
}

View File

@@ -102,7 +102,7 @@ func (this *AckList) Flush(current uint32, rto uint32) {
if this.nextFlush[i] <= current {
seg.PutNumber(this.numbers[i])
seg.PutTimestamp(this.timestamps[i])
this.nextFlush[i] = current + rto/2
this.nextFlush[i] = current + rto/4
}
}
if seg.Count > 0 {

View File

@@ -48,6 +48,13 @@ func NewDataSegment() *DataSegment {
return new(DataSegment)
}
func (this *DataSegment) SetData(b []byte) {
if this.Data == nil {
this.Data = alloc.NewLocalBuffer(1600)
}
this.Data.Clear().Append(b)
}
func (this *DataSegment) Bytes(b []byte) []byte {
b = serial.Uint16ToBytes(this.Conv, b)
b = append(b, byte(CommandData), byte(this.Option))
@@ -181,7 +188,7 @@ func ReadSegment(buf []byte) (Segment, []byte) {
if len(buf) < dataLen {
return nil, nil
}
seg.Data = AllocateBuffer().Clear().Append(buf[:dataLen])
seg.SetData(buf[:dataLen])
buf = buf[dataLen:]
return seg, buf

View File

@@ -10,9 +10,10 @@ type SendingWindow struct {
len uint32
last uint32
data []*DataSegment
prev []uint32
next []uint32
data []DataSegment
inuse []bool
prev []uint32
next []uint32
totalInFlightSize uint32
writer SegmentWriter
@@ -25,9 +26,10 @@ func NewSendingWindow(size uint32, writer SegmentWriter, onPacketLoss func(uint3
cap: size,
len: 0,
last: 0,
data: make([]*DataSegment, size),
data: make([]DataSegment, size),
prev: make([]uint32, size),
next: make([]uint32, size),
inuse: make([]bool, size),
writer: writer,
onPacketLoss: onPacketLoss,
}
@@ -50,9 +52,13 @@ func (this *SendingWindow) IsFull() bool {
return this.len == this.cap
}
func (this *SendingWindow) Push(seg *DataSegment) {
func (this *SendingWindow) Push(number uint32, data []byte) {
pos := (this.start + this.len) % this.cap
this.data[pos] = seg
this.data[pos].SetData(data)
this.data[pos].Number = number
this.data[pos].timeout = 0
this.data[pos].transmit = 0
this.inuse[pos] = true
if this.len > 0 {
this.next[this.last] = pos
this.prev[pos] = this.last
@@ -61,8 +67,8 @@ func (this *SendingWindow) Push(seg *DataSegment) {
this.len++
}
func (this *SendingWindow) First() *DataSegment {
return this.data[this.start]
func (this *SendingWindow) FirstNumber() uint32 {
return this.data[this.start].Number
}
func (this *SendingWindow) Clear(una uint32) {
@@ -77,13 +83,11 @@ func (this *SendingWindow) Remove(idx uint32) {
}
pos := (this.start + idx) % this.cap
seg := this.data[pos]
if seg == nil {
if !this.inuse[pos] {
return
}
this.inuse[pos] = false
this.totalInFlightSize--
seg.Release()
this.data[pos] = nil
if pos == this.start && pos == this.last {
this.len = 0
this.start = 0
@@ -109,7 +113,7 @@ func (this *SendingWindow) HandleFastAck(number uint32, rto uint32) {
}
for i := this.start; ; i = this.next[i] {
seg := this.data[i]
seg := &this.data[i]
if number-seg.Number > 0x7FFFFFFF {
break
}
@@ -133,7 +137,7 @@ func (this *SendingWindow) Flush(current uint32, rto uint32, maxInFlightSize uin
var inFlightSize uint32
for i := this.start; ; i = this.next[i] {
segment := this.data[i]
segment := &this.data[i]
needsend := false
if current-segment.timeout < 0x7FFFFFFF {
if segment.transmit == 0 {
@@ -205,7 +209,7 @@ func (this *SendingWorker) ProcessReceivingNextWithoutLock(nextNumber uint32) {
func (this *SendingWorker) FindFirstUnacknowledged() {
v := this.firstUnacknowledged
if !this.window.IsEmpty() {
this.firstUnacknowledged = this.window.First().Number
this.firstUnacknowledged = this.window.FirstNumber()
} else {
this.firstUnacknowledged = this.nextNumber
}
@@ -264,12 +268,7 @@ func (this *SendingWorker) Push(b []byte) int {
} else {
size = len(b)
}
seg := NewDataSegment()
seg.Data = AllocateBuffer().Clear().Append(b[:size])
seg.Number = this.nextNumber
seg.timeout = 0
seg.transmit = 0
this.window.Push(seg)
this.window.Push(this.nextNumber, b[:size])
this.nextNumber++
b = b[size:]
nBytes += size

View File

@@ -11,37 +11,27 @@ func TestSendingWindow(t *testing.T) {
assert := assert.On(t)
window := NewSendingWindow(5, nil, nil)
window.Push(&DataSegment{
Number: 0,
})
window.Push(&DataSegment{
Number: 1,
})
window.Push(&DataSegment{
Number: 2,
})
window.Push(0, []byte{})
window.Push(1, []byte{})
window.Push(2, []byte{})
assert.Int(window.Len()).Equals(3)
window.Remove(1)
assert.Int(window.Len()).Equals(3)
assert.Uint32(window.First().Number).Equals(0)
assert.Uint32(window.FirstNumber()).Equals(0)
window.Remove(0)
assert.Int(window.Len()).Equals(1)
assert.Uint32(window.First().Number).Equals(2)
assert.Uint32(window.FirstNumber()).Equals(2)
window.Remove(0)
assert.Int(window.Len()).Equals(0)
window.Push(&DataSegment{
Number: 4,
})
window.Push(4, []byte{})
assert.Int(window.Len()).Equals(1)
assert.Uint32(window.First().Number).Equals(4)
assert.Uint32(window.FirstNumber()).Equals(4)
window.Push(&DataSegment{
Number: 5,
})
window.Push(5, []byte{})
assert.Int(window.Len()).Equals(2)
window.Remove(1)

View File

@@ -17,6 +17,7 @@ package tcp
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import v2ray_core_common_loader "v2ray.com/core/common/loader"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
@@ -39,7 +40,8 @@ func (*ConnectionReuse) ProtoMessage() {}
func (*ConnectionReuse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type Config struct {
ConnectionReuse *ConnectionReuse `protobuf:"bytes,1,opt,name=connection_reuse,json=connectionReuse" json:"connection_reuse,omitempty"`
ConnectionReuse *ConnectionReuse `protobuf:"bytes,1,opt,name=connection_reuse,json=connectionReuse" json:"connection_reuse,omitempty"`
HeaderSettings *v2ray_core_common_loader.TypedSettings `protobuf:"bytes,2,opt,name=header_settings,json=headerSettings" json:"header_settings,omitempty"`
}
func (m *Config) Reset() { *m = Config{} }
@@ -54,6 +56,13 @@ func (m *Config) GetConnectionReuse() *ConnectionReuse {
return nil
}
func (m *Config) GetHeaderSettings() *v2ray_core_common_loader.TypedSettings {
if m != nil {
return m.HeaderSettings
}
return nil
}
func init() {
proto.RegisterType((*ConnectionReuse)(nil), "v2ray.core.transport.internet.tcp.ConnectionReuse")
proto.RegisterType((*Config)(nil), "v2ray.core.transport.internet.tcp.Config")
@@ -62,17 +71,21 @@ func init() {
func init() { proto.RegisterFile("v2ray.com/core/transport/internet/tcp/config.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 191 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x8f, 0x41, 0x6b, 0x83, 0x40,
0x10, 0x85, 0x91, 0x82, 0x94, 0xf5, 0x60, 0xf1, 0x50, 0x7a, 0x6c, 0x85, 0x42, 0x7b, 0xd9, 0x85,
0xed, 0xa9, 0x57, 0xfd, 0x03, 0xc1, 0x63, 0x20, 0x04, 0x1d, 0x26, 0x22, 0xc4, 0x99, 0x65, 0x9c,
0x04, 0xf2, 0xef, 0x83, 0x26, 0x0a, 0xc9, 0xc5, 0xe3, 0x2e, 0xdf, 0xf7, 0xde, 0x3c, 0xe3, 0xcf,
0x5e, 0xea, 0x8b, 0x05, 0xee, 0x1d, 0xb0, 0xa0, 0x53, 0xa9, 0x69, 0x08, 0x2c, 0xea, 0x3a, 0x52,
0x14, 0x42, 0x75, 0x0a, 0xc1, 0x01, 0xd3, 0xa1, 0x6b, 0x6d, 0x10, 0x56, 0xce, 0xbe, 0x66, 0x47,
0xd0, 0x2e, 0xbc, 0x9d, 0x79, 0xab, 0x10, 0xf2, 0x5f, 0x93, 0x96, 0x4c, 0x84, 0xa0, 0x1d, 0x53,
0x85, 0xa7, 0x01, 0xb3, 0x77, 0x13, 0x23, 0xd5, 0xcd, 0x11, 0x3f, 0xa2, 0xcf, 0xe8, 0xe7, 0xb5,
0xba, 0xbf, 0xf2, 0xd6, 0xc4, 0xe5, 0x94, 0x9e, 0xed, 0xcc, 0x1b, 0x2c, 0xd2, 0x5e, 0x46, 0x6b,
0x62, 0x13, 0xef, 0xed, 0x6a, 0xa5, 0x7d, 0xea, 0xab, 0x52, 0x78, 0xfc, 0x28, 0xfe, 0xcd, 0x37,
0x70, 0xbf, 0x9e, 0x54, 0x24, 0xb7, 0x7b, 0x36, 0xe3, 0xd8, 0xed, 0x8b, 0x42, 0x68, 0xe2, 0x69,
0xf8, 0xdf, 0x35, 0x00, 0x00, 0xff, 0xff, 0xc1, 0x16, 0x42, 0x0b, 0x2e, 0x01, 0x00, 0x00,
// 249 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x90, 0xc1, 0x4a, 0xc3, 0x60,
0x10, 0x84, 0x89, 0x42, 0x90, 0xbf, 0x60, 0x24, 0x07, 0x29, 0x9e, 0xb4, 0x20, 0xd5, 0xcb, 0xfe,
0x10, 0x4f, 0x5e, 0xdb, 0x17, 0x28, 0xd1, 0x93, 0x20, 0x25, 0xdd, 0xae, 0x35, 0xd0, 0xec, 0xfe,
0x6c, 0x56, 0x21, 0x8f, 0xe6, 0xdb, 0x49, 0x92, 0xa6, 0x94, 0x5c, 0x7a, 0x5c, 0x98, 0x99, 0xfd,
0x66, 0x5c, 0xf6, 0x9b, 0x69, 0xd1, 0x00, 0x4a, 0xe5, 0x51, 0x94, 0xbc, 0x69, 0xc1, 0x75, 0x10,
0x35, 0x5f, 0xb2, 0x91, 0x32, 0x99, 0x37, 0x0c, 0x1e, 0x85, 0xbf, 0xca, 0x1d, 0x04, 0x15, 0x93,
0xf4, 0x61, 0xf0, 0x28, 0xc1, 0x51, 0x0f, 0x83, 0x1e, 0x0c, 0xc3, 0xdd, 0x7c, 0x14, 0x8b, 0x52,
0x55, 0xc2, 0x7e, 0x2f, 0xc5, 0x96, 0xd4, 0x5b, 0x13, 0xa8, 0xcf, 0x9a, 0x3d, 0xbb, 0x64, 0x29,
0xcc, 0x84, 0x56, 0x0a, 0xe7, 0xf4, 0x53, 0x53, 0x7a, 0xeb, 0x62, 0xe2, 0x62, 0xb3, 0xa7, 0x69,
0x74, 0x1f, 0x3d, 0x5d, 0xe5, 0x87, 0x6b, 0xf6, 0x17, 0xb9, 0x78, 0xd9, 0x71, 0xa4, 0x9f, 0xee,
0x06, 0x8f, 0xae, 0xb5, 0xb6, 0xb6, 0x4e, 0x3c, 0xc9, 0x32, 0x38, 0x0b, 0x07, 0xa3, 0x87, 0x79,
0x82, 0x23, 0x82, 0x95, 0x4b, 0xbe, 0xa9, 0x25, 0x5d, 0xd7, 0x64, 0x56, 0xf2, 0xae, 0x9e, 0x5e,
0x74, 0xe9, 0xf3, 0xd3, 0xf4, 0xbe, 0x13, 0xf4, 0x9d, 0xe0, 0xbd, 0x09, 0xb4, 0x7d, 0x3b, 0xc8,
0xf3, 0xeb, 0xde, 0x3f, 0xdc, 0x8b, 0x57, 0xf7, 0x88, 0x52, 0x9d, 0x67, 0x5b, 0x4c, 0xfa, 0x86,
0xab, 0x76, 0x9c, 0x8f, 0x4b, 0xc3, 0xb0, 0x89, 0xbb, 0xa1, 0x5e, 0xfe, 0x03, 0x00, 0x00, 0xff,
0xff, 0xdf, 0xaf, 0x5e, 0x9f, 0xaa, 0x01, 0x00, 0x00,
}

View File

@@ -5,9 +5,12 @@ option go_package = "tcp";
option java_package = "com.v2ray.core.transport.internet.tcp";
option java_outer_classname = "ConfigProto";
import "v2ray.com/core/common/loader/type.proto";
message ConnectionReuse {
bool enable = 1;
}
message Config {
ConnectionReuse connection_reuse = 1;
v2ray.core.common.loader.TypedSettings header_settings = 2;
}

View File

@@ -1,6 +1,7 @@
package tcp
import (
"errors"
"net"
"crypto/tls"
@@ -36,20 +37,31 @@ 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
}
tlsConfig, ok := securitySettings.(*v2tls.Config)
if ok {
config := tlsConfig.GetTLSConfig()
if dest.Address.Family().IsDomain() {
config.ServerName = dest.Address.Domain()
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
}
conn = tls.Client(conn, config)
tlsConfig, ok := securitySettings.(*v2tls.Config)
if ok {
config := tlsConfig.GetTLSConfig()
if dest.Address.Family().IsDomain() {
config.ServerName = dest.Address.Domain()
}
conn = tls.Client(conn, config)
}
}
if tcpSettings.HeaderSettings != nil {
headerConfig, err := tcpSettings.HeaderSettings.GetInstance()
if err != nil {
return nil, errors.New("TCP: Failed to get header settings: " + err.Error())
}
auth, err := internet.CreateConnectionAuthenticator(tcpSettings.HeaderSettings.Type, headerConfig)
if err != nil {
return nil, errors.New("TCP: Failed to create header authenticator: " + err.Error())
}
conn = auth.Client(conn)
}
}
return NewConnection(id, conn, globalCache, tcpSettings), nil

View File

@@ -28,6 +28,7 @@ type TCPListener struct {
listener *net.TCPListener
awaitingConns chan *ConnectionWithError
tlsConfig *tls.Config
authConfig internet.ConnectionAuthenticator
config *Config
}
@@ -62,6 +63,17 @@ func ListenTCP(address v2net.Address, port v2net.Port, options internet.ListenOp
l.tlsConfig = tlsConfig.GetTLSConfig()
}
}
if tcpSettings.HeaderSettings != nil {
headerConfig, err := tcpSettings.HeaderSettings.GetInstance()
if err != nil {
return nil, errors.New("TCP: Failed to get header settings: " + err.Error())
}
auth, err := internet.CreateConnectionAuthenticator(tcpSettings.HeaderSettings.Type, headerConfig)
if err != nil {
return nil, errors.New("TCP: Failed to create header authenticator: " + err.Error())
}
l.authConfig = auth
}
go l.KeepAccepting()
return l, nil
}
@@ -77,9 +89,6 @@ func (this *TCPListener) Accept() (internet.Connection, error) {
return nil, connErr.err
}
conn := connErr.conn
if this.tlsConfig != nil {
conn = tls.Server(conn, this.tlsConfig)
}
return NewConnection("", conn, this, this.config), nil
case <-time.After(time.Second * 2):
}
@@ -95,6 +104,12 @@ func (this *TCPListener) KeepAccepting() {
this.Unlock()
break
}
if this.tlsConfig != nil {
conn = tls.Server(conn, this.tlsConfig)
}
if this.authConfig != nil {
conn = this.authConfig.Server(conn)
}
select {
case this.awaitingConns <- &ConnectionWithError{
conn: conn,

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