You've already forked v2ray-core
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e0d54fd4c | ||
|
|
d343cb1ee6 | ||
|
|
7a09fcdc2f | ||
|
|
b0d009664a | ||
|
|
3d2431d21c | ||
|
|
d04e145442 | ||
|
|
d75cb28413 | ||
|
|
9f68062d48 | ||
|
|
d70b997d84 | ||
|
|
dc0cbce6e1 | ||
|
|
edc5bbbb72 | ||
|
|
66e8090d3a | ||
|
|
805bbe5fe4 | ||
|
|
31be091a55 | ||
|
|
f108633e2e | ||
|
|
d2263a452d | ||
|
|
dfc03ff939 | ||
|
|
13dde1799d | ||
|
|
c84629c374 | ||
|
|
817cc8d82e | ||
|
|
13ab2622c5 | ||
|
|
0727aa0da9 | ||
|
|
3a15f799c2 | ||
|
|
bae0de7d95 | ||
|
|
80312627c4 | ||
|
|
3d167a6855 | ||
|
|
010f34c76c | ||
|
|
0747203132 | ||
|
|
1600a59254 | ||
|
|
c5a92e00ef | ||
|
|
68b85cce60 | ||
|
|
9d2407f4e4 | ||
|
|
cdb0debcb0 | ||
|
|
5f3f173b5e | ||
|
|
35aa16d40d | ||
|
|
de4836c720 | ||
|
|
63d3c9fa30 | ||
|
|
687e008c9a | ||
|
|
43dacc3936 | ||
|
|
aabb9137e1 | ||
|
|
33d2513e3c | ||
|
|
5b58066345 | ||
|
|
d3f323e24b | ||
|
|
187688cacb | ||
|
|
72339a3509 | ||
|
|
ac3b91a877 | ||
|
|
5e1c6fe816 | ||
|
|
a54c39b4ac | ||
|
|
c221802963 | ||
|
|
be4f3d0772 | ||
|
|
fbb44e7e02 | ||
|
|
369256c82f | ||
|
|
531be77a59 | ||
|
|
97dc7b30de | ||
|
|
360c222c1c | ||
|
|
1fb9a911cd | ||
|
|
6006fd9ca7 | ||
|
|
59fa064cae | ||
|
|
3373e62193 | ||
|
|
2c2c569c77 | ||
|
|
687ae6c50e | ||
|
|
35ba8710e0 | ||
|
|
a7ef82ffbc | ||
|
|
ca980f5718 | ||
|
|
aae99a8e98 | ||
|
|
f37b04a690 | ||
|
|
e13c97d162 | ||
|
|
2af4b16913 | ||
|
|
2320bc3304 | ||
|
|
751a105324 | ||
|
|
b3bbd80674 | ||
|
|
4e80ed05d9 | ||
|
|
426a58707f | ||
|
|
9b4d9cf0e7 | ||
|
|
f049b3cc2b | ||
|
|
b81d091fb8 | ||
|
|
9bd8822668 | ||
|
|
ad59e56925 | ||
|
|
4a67587873 | ||
|
|
8b936bc816 |
@@ -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
1
all.go
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{} }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
131
app/proxy/proxy.go
Normal 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) {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
26
config.pb.go
26
config.pb.go
@@ -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"`
|
||||
}
|
||||
|
||||
21
config.proto
21
config.proto
@@ -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;
|
||||
}
|
||||
2
core.go
2
core.go
@@ -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."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
168
proxy/shadowsocks/client.go
Normal 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)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
12
proxy/shadowsocks/init.go
Normal 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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
5
proxy/shadowsocks/shadowsocks_test.go
Normal file
5
proxy/shadowsocks/shadowsocks_test.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package shadowsocks_test
|
||||
|
||||
import (
|
||||
// . "v2ray.com/core/proxy/shadowsocks"
|
||||
)
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -n "${TRAVIS_TAG}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
FAIL=0
|
||||
|
||||
V2RAY_OUT=${GOPATH}/out/v2ray
|
||||
|
||||
39
testing/scenarios/data/test_6_client.json
Normal file
39
testing/scenarios/data/test_6_client.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
66
tools/geoip/geoip.pb.go
Normal 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
10
tools/geoip/geoip.proto
Normal 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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
96
transport/internet/authenticators/http/config.go
Normal file
96
transport/internet/authenticators/http/config.go
Normal 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()
|
||||
}
|
||||
216
transport/internet/authenticators/http/config.pb.go
Normal file
216
transport/internet/authenticators/http/config.pb.go
Normal 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,
|
||||
}
|
||||
61
transport/internet/authenticators/http/config.proto
Normal file
61
transport/internet/authenticators/http/config.proto
Normal 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;
|
||||
}
|
||||
206
transport/internet/authenticators/http/http.go
Normal file
206
transport/internet/authenticators/http/http.go
Normal 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{})
|
||||
}
|
||||
46
transport/internet/authenticators/http/http_test.go
Normal file
46
transport/internet/authenticators/http/http_test.go
Normal 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")
|
||||
}
|
||||
@@ -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{})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
36
transport/internet/conn_authenticator.go
Normal file
36
transport/internet/conn_authenticator.go
Normal 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
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user