Compare commits

..

52 Commits

Author SHA1 Message Date
Darien Raymond
b0d009664a Fix HTTP serialization 2016-11-07 10:47:30 +01:00
Darien Raymond
3d2431d21c update test data 2016-11-07 10:38:43 +01:00
Darien Raymond
d04e145442 Update version 2016-11-06 21:05:19 +01:00
Darien Raymond
d75cb28413 fix tcp encoding 2016-11-06 21:04:56 +01:00
Darien Raymond
9f68062d48 refine http header parsing 2016-11-06 15:48:25 +01:00
Darien Raymond
d70b997d84 test case for shadowsocks UDP 2016-11-06 14:32:04 +01:00
Darien Raymond
dc0cbce6e1 optimize pickString 2016-11-06 14:04:44 +01:00
Darien Raymond
edc5bbbb72 add more default headers 2016-11-06 13:55:06 +01:00
Darien Raymond
66e8090d3a update http header 2016-11-06 13:38:32 +01:00
Darien Raymond
805bbe5fe4 default user agent strings 2016-11-06 00:42:54 +01:00
Darien Raymond
31be091a55 simplify http header parsing 2016-11-05 15:14:55 +01:00
Darien Raymond
f108633e2e ssr compatibility 2016-11-05 01:50:51 +01:00
Darien Raymond
d2263a452d update http header json config 2016-11-05 01:28:15 +01:00
Darien Raymond
dfc03ff939 support noop connection auth 2016-11-05 01:15:32 +01:00
Darien Raymond
13dde1799d comments 2016-11-05 01:01:46 +01:00
Darien Raymond
c84629c374 add shadowsocks in json config 2016-11-05 01:01:30 +01:00
Darien Raymond
817cc8d82e fix shadowsocks conf 2016-11-05 01:01:07 +01:00
Darien Raymond
13ab2622c5 shadowsocks test 2016-11-05 01:00:54 +01:00
Darien Raymond
0727aa0da9 simplify imports 2016-11-05 01:00:20 +01:00
Darien Raymond
3a15f799c2 prevent deadlock in shadowsocks 2016-11-05 01:00:09 +01:00
Darien Raymond
bae0de7d95 test case for http auth 2016-11-04 21:59:19 +01:00
Darien Raymond
80312627c4 fix typo 2016-11-04 10:49:43 +01:00
Darien Raymond
3d167a6855 json config for http header 2016-11-04 10:49:18 +01:00
Darien Raymond
010f34c76c allow single side auth 2016-11-03 23:14:27 +01:00
Darien Raymond
0747203132 refine http header 2016-11-02 22:26:21 +01:00
Darien Raymond
1600a59254 enable tcp for shadowsocks server 2016-11-02 22:19:01 +01:00
Darien Raymond
c5a92e00ef dont reuse shadowsock connections 2016-11-02 22:18:25 +01:00
Darien Raymond
68b85cce60 remove unused code 2016-11-02 16:42:02 +01:00
Darien Raymond
9d2407f4e4 fix test break 2016-11-02 16:41:02 +01:00
Darien Raymond
cdb0debcb0 register shadowsocks client factory 2016-11-02 16:38:09 +01:00
Darien Raymond
5f3f173b5e shadowsocks client factory 2016-11-02 16:33:04 +01:00
Darien Raymond
35aa16d40d refine shadowsocks user 2016-11-02 16:22:29 +01:00
Darien Raymond
de4836c720 refine ota settings in conf 2016-11-02 16:21:20 +01:00
Darien Raymond
63d3c9fa30 typo 2016-11-02 16:18:29 +01:00
Darien Raymond
687e008c9a refine shadowsocks ota settings 2016-11-02 16:17:57 +01:00
Darien Raymond
43dacc3936 remove small buffer pool 2016-11-01 12:37:35 +01:00
Darien Raymond
aabb9137e1 remove unused code 2016-11-01 12:34:07 +01:00
Darien Raymond
33d2513e3c reduce memory allocation in kcp 2016-11-01 12:07:20 +01:00
Darien Raymond
5b58066345 use local buffer in UDP hub 2016-11-01 11:46:34 +01:00
Darien Raymond
d3f323e24b response factory 2016-11-01 00:42:55 +01:00
Darien Raymond
187688cacb fix type def 2016-11-01 00:42:44 +01:00
Darien Raymond
72339a3509 fix TCP conn reuse with tls 2016-11-01 00:41:46 +01:00
Darien Raymond
ac3b91a877 http authenticator 2016-10-31 22:26:46 +01:00
Darien Raymond
5e1c6fe816 bug fix 2016-10-31 16:46:47 +01:00
Darien Raymond
a54c39b4ac config for shadowsocks 2016-10-31 16:46:15 +01:00
Darien Raymond
c221802963 shadowsocks client 2016-10-31 16:35:18 +01:00
Darien Raymond
be4f3d0772 refine shadowsocks server 2016-10-31 15:24:28 +01:00
Darien Raymond
fbb44e7e02 fix string checking logic 2016-10-24 16:59:43 +02:00
Darien Raymond
369256c82f include user when dispatch requests in VMess. 2016-10-24 16:57:16 +02:00
Darien Raymond
531be77a59 remove test case depending on external resources 2016-10-24 16:49:41 +02:00
Darien Raymond
97dc7b30de remove test cases depending on external resources 2016-10-24 16:48:16 +02:00
Darien Raymond
360c222c1c try again to skip coverage on release 2016-10-24 16:45:02 +02:00
53 changed files with 1906 additions and 989 deletions

1
all.go
View File

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

View File

@@ -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}))
}

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

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

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

View File

@@ -118,10 +118,11 @@ func (this *ChunkWriter) Release() {
this.auth = nil
}
func (this *ChunkWriter) Write(payload *alloc.Buffer) (int, error) {
func (this *ChunkWriter) Write(payload *alloc.Buffer) error {
totalLength := payload.Len()
authBytes := this.auth.Authenticate(nil, payload.Bytes())
payload.Prepend(authBytes)
payload.SliceBack(AuthSize)
this.auth.Authenticate(payload.Value[:0], payload.Value[AuthSize:])
payload.PrependUint16(uint16(totalLength))
return this.writer.Write(payload.Bytes())
_, err := this.writer.Write(payload.Bytes())
return err
}

View File

@@ -26,12 +26,11 @@ func TestNormalChunkReading(t *testing.T) {
func TestNormalChunkWriting(t *testing.T) {
assert := assert.On(t)
buffer := alloc.NewBuffer().Clear()
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})))
nBytes, err := writer.Write(alloc.NewBuffer().Clear().Append([]byte{11, 12, 13, 14, 15, 16, 17, 18}))
err := writer.Write(alloc.NewLocalBuffer(256).Clear().Append([]byte{11, 12, 13, 14, 15, 16, 17, 18}))
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(buffer.Len())
assert.Bytes(buffer.Value).Equals([]byte{0, 8, 39, 228, 69, 96, 133, 39, 254, 26, 201, 70, 11, 12, 13, 14, 15, 16, 17, 18})
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,6 @@ import (
"v2ray.com/core/common/retry"
"v2ray.com/core/proxy"
"v2ray.com/core/proxy/registry"
"v2ray.com/core/proxy/vmess"
"v2ray.com/core/proxy/vmess/encoding"
vmessio "v2ray.com/core/proxy/vmess/io"
"v2ray.com/core/transport/internet"
@@ -172,7 +171,7 @@ func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.
serverList := protocol.NewServerList()
for _, rec := range vOutConfig.Receiver {
serverList.AddServer(protocol.NewServerSpecFromPB(vmess.NewAccount, *rec))
serverList.AddServer(protocol.NewServerSpecFromPB(*rec))
}
handler := &VMessOutboundHandler{
serverList: serverList,

View File

@@ -1,6 +1,6 @@
#!/bin/bash
if [ -z ${TRAVIS_TAG+x} ]; then
if [ -n "${TRAVIS_TAG}" ]; then
exit 0
fi

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,11 @@ var (
"srtp": func() interface{} { return new(SRTPAuthenticator) },
"utp": func() interface{} { return new(UTPAuthenticator) },
}, "type", "")
tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
"none": func() interface{} { return new(NoOpConnectionAuthenticator) },
"http": func() interface{} { return new(HTTPAuthenticator) },
}, "type", "")
)
type KCPConfig struct {
@@ -93,7 +98,8 @@ func (this *KCPConfig) Build() (*loader.TypedSettings, error) {
}
type TCPConfig struct {
ConnectionReuse *bool `json:"connectionReuse"`
ConnectionReuse *bool `json:"connectionReuse"`
HeaderConfig json.RawMessage `json:"header"`
}
func (this *TCPConfig) Build() (*loader.TypedSettings, error) {
@@ -103,6 +109,18 @@ func (this *TCPConfig) Build() (*loader.TypedSettings, error) {
Enable: *this.ConnectionReuse,
}
}
if len(this.HeaderConfig) > 0 {
headerConfig, _, err := tcpHeaderLoader.Load(this.HeaderConfig)
if err != nil {
return nil, errors.New("TCP|Config: Failed to parse header config: " + err.Error())
}
ts, err := headerConfig.(Buildable).Build()
if err != nil {
return nil, errors.New("Failed to get TCP authenticator config: " + err.Error())
}
config.HeaderSettings = ts
}
return loader.NewTypedSettings(config), nil
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package tcp
import (
"errors"
"net"
"crypto/tls"
@@ -36,20 +37,31 @@ func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOpti
if err != nil {
return nil, err
}
}
if options.Stream != nil && options.Stream.HasSecuritySettings() {
securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
if err != nil {
log.Error("TCP: Failed to get security settings: ", err)
return nil, err
}
tlsConfig, ok := securitySettings.(*v2tls.Config)
if ok {
config := tlsConfig.GetTLSConfig()
if dest.Address.Family().IsDomain() {
config.ServerName = dest.Address.Domain()
if options.Stream != nil && options.Stream.HasSecuritySettings() {
securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
if err != nil {
log.Error("TCP: Failed to get security settings: ", err)
return nil, err
}
conn = tls.Client(conn, config)
tlsConfig, ok := securitySettings.(*v2tls.Config)
if ok {
config := tlsConfig.GetTLSConfig()
if dest.Address.Family().IsDomain() {
config.ServerName = dest.Address.Domain()
}
conn = tls.Client(conn, config)
}
}
if tcpSettings.HeaderSettings != nil {
headerConfig, err := tcpSettings.HeaderSettings.GetInstance()
if err != nil {
return nil, errors.New("TCP: Failed to get header settings: " + err.Error())
}
auth, err := internet.CreateConnectionAuthenticator(tcpSettings.HeaderSettings.Type, headerConfig)
if err != nil {
return nil, errors.New("TCP: Failed to create header authenticator: " + err.Error())
}
conn = auth.Client(conn)
}
}
return NewConnection(id, conn, globalCache, tcpSettings), nil

View File

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

View File

@@ -18,6 +18,7 @@ type UDPHub struct {
conn *net.UDPConn
option ListenOption
accepting bool
pool *alloc.BufferPool
}
type ListenOption struct {
@@ -48,6 +49,7 @@ func ListenUDP(address v2net.Address, port v2net.Port, option ListenOption) (*UD
hub := &UDPHub{
conn: udpConn,
option: option,
pool: alloc.NewBufferPool(2048, 64),
}
go hub.start()
return hub, nil
@@ -75,7 +77,7 @@ func (this *UDPHub) start() {
oobBytes := make([]byte, 256)
for this.Running() {
buffer := alloc.NewBuffer()
buffer := this.pool.Allocate()
nBytes, noob, _, addr, err := ReadUDPMsg(this.conn, buffer.Value, oobBytes)
if err != nil {
log.Info("UDP|Hub: Failed to read UDP msg: ", err)

View File

@@ -13,229 +13,6 @@ import (
. "v2ray.com/core/transport/internet/ws"
)
func Test_Connect_ws(t *testing.T) {
assert := assert.On(t)
conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 80), internet.DialerOptions{
Stream: &internet.StreamConfig{
Network: v2net.Network_WebSocket,
NetworkSettings: []*internet.NetworkSettings{
{
Network: v2net.Network_WebSocket,
Settings: loader.NewTypedSettings(&Config{
Path: "",
}),
},
},
},
})
assert.Error(err).IsNil()
conn.Write([]byte("echo"))
s := make(chan int)
go func() {
buf := make([]byte, 4)
conn.Read(buf)
str := string(buf)
if str != "echo" {
assert.Fail("Data mismatch")
}
s <- 0
}()
<-s
conn.Close()
}
func Test_Connect_wss(t *testing.T) {
assert := assert.On(t)
conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 443), internet.DialerOptions{
Stream: &internet.StreamConfig{
Network: v2net.Network_WebSocket,
NetworkSettings: []*internet.NetworkSettings{
{
Network: v2net.Network_WebSocket,
Settings: loader.NewTypedSettings(&Config{
Path: "",
}),
},
},
SecurityType: loader.GetType(new(v2tls.Config)),
},
})
assert.Error(err).IsNil()
conn.Write([]byte("echo"))
s := make(chan int)
go func() {
buf := make([]byte, 4)
conn.Read(buf)
str := string(buf)
if str != "echo" {
assert.Fail("Data mismatch")
}
s <- 0
}()
<-s
conn.Close()
}
func Test_Connect_wss_1_nil(t *testing.T) {
assert := assert.On(t)
conn, err := Dial(nil, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 443), internet.DialerOptions{
Stream: &internet.StreamConfig{
Network: v2net.Network_WebSocket,
NetworkSettings: []*internet.NetworkSettings{
{
Network: v2net.Network_WebSocket,
Settings: loader.NewTypedSettings(&Config{
Path: "",
}),
},
},
SecurityType: loader.GetType(new(v2tls.Config)),
},
})
assert.Error(err).IsNil()
conn.Write([]byte("echo"))
s := make(chan int)
go func() {
buf := make([]byte, 4)
conn.Read(buf)
str := string(buf)
if str != "echo" {
assert.Fail("Data mismatch")
}
s <- 0
}()
<-s
conn.Close()
}
func Test_Connect_ws_guess(t *testing.T) {
assert := assert.On(t)
conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 80), internet.DialerOptions{
Stream: &internet.StreamConfig{
Network: v2net.Network_WebSocket,
NetworkSettings: []*internet.NetworkSettings{
{
Network: v2net.Network_WebSocket,
Settings: loader.NewTypedSettings(&Config{
Path: "",
}),
},
},
},
})
assert.Error(err).IsNil()
conn.Write([]byte("echo"))
s := make(chan int)
go func() {
buf := make([]byte, 4)
conn.Read(buf)
str := string(buf)
if str != "echo" {
assert.Fail("Data mismatch")
}
s <- 0
}()
<-s
conn.Close()
}
func Test_Connect_wss_guess(t *testing.T) {
assert := assert.On(t)
conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 443), internet.DialerOptions{
Stream: &internet.StreamConfig{
Network: v2net.Network_WebSocket,
NetworkSettings: []*internet.NetworkSettings{
{
Network: v2net.Network_WebSocket,
Settings: loader.NewTypedSettings(&Config{
Path: "",
}),
},
},
SecurityType: loader.GetType(new(v2tls.Config)),
},
})
assert.Error(err).IsNil()
conn.Write([]byte("echo"))
s := make(chan int)
go func() {
buf := make([]byte, 4)
conn.Read(buf)
str := string(buf)
if str != "echo" {
assert.Fail("Data mismatch")
}
s <- 0
}()
<-s
conn.Close()
}
func Test_Connect_wss_guess_fail(t *testing.T) {
assert := assert.On(t)
_, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("static.kkdev.org"), 443), internet.DialerOptions{
Stream: &internet.StreamConfig{
Network: v2net.Network_WebSocket,
NetworkSettings: []*internet.NetworkSettings{
{
Network: v2net.Network_WebSocket,
Settings: loader.NewTypedSettings(&Config{
Path: "",
}),
},
},
SecurityType: loader.GetType(new(v2tls.Config)),
},
})
assert.Error(err).IsNotNil()
}
func Test_Connect_wss_guess_reuse(t *testing.T) {
assert := assert.On(t)
i := 3
for i != 0 {
conn, err := Dial(v2net.AnyIP, v2net.TCPDestination(v2net.DomainAddress("echo.websocket.org"), 443), internet.DialerOptions{
Stream: &internet.StreamConfig{
Network: v2net.Network_WebSocket,
NetworkSettings: []*internet.NetworkSettings{
{
Network: v2net.Network_WebSocket,
Settings: loader.NewTypedSettings(&Config{
Path: "",
ConnectionReuse: &ConnectionReuse{
Enable: true,
},
}),
},
},
SecurityType: loader.GetType(new(v2tls.Config)),
},
})
assert.Error(err).IsNil()
conn.Write([]byte("echo"))
s := make(chan int)
go func() {
buf := make([]byte, 4)
conn.Read(buf)
str := string(buf)
if str != "echo" {
assert.Fail("Data mismatch")
}
s <- 0
}()
<-s
if i == 0 {
conn.SetDeadline(time.Now())
conn.SetReadDeadline(time.Now())
conn.SetWriteDeadline(time.Now())
conn.SetReusable(false)
}
conn.Close()
i--
}
}
func Test_listenWSAndDial(t *testing.T) {
assert := assert.On(t)
listen, err := ListenWS(v2net.DomainAddress("localhost"), 13146, internet.ListenOptions{