mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-22 01:57:12 -05:00
shadowsocks client
This commit is contained in:
parent
be4f3d0772
commit
c221802963
@ -1,7 +1,11 @@
|
|||||||
package shadowsocks
|
package shadowsocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"sync"
|
||||||
"v2ray.com/core/common/alloc"
|
"v2ray.com/core/common/alloc"
|
||||||
|
v2io "v2ray.com/core/common/io"
|
||||||
"v2ray.com/core/common/log"
|
"v2ray.com/core/common/log"
|
||||||
v2net "v2ray.com/core/common/net"
|
v2net "v2ray.com/core/common/net"
|
||||||
"v2ray.com/core/common/protocol"
|
"v2ray.com/core/common/protocol"
|
||||||
@ -39,10 +43,96 @@ func (this *Client) Dispatch(destination v2net.Destination, payload *alloc.Buffe
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Shadowsocks|Client: Failed to find an available destination:", err)
|
return errors.New("Shadowsocks|Client: Failed to find an available destination:" + err.Error())
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
log.Info("Shadowsocks|Client: Tunneling request to ", destination, " via ", server.Destination())
|
log.Info("Shadowsocks|Client: Tunneling request to ", destination, " via ", server.Destination())
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferedWriter.SetCached(false)
|
||||||
|
v2io.Pipe(ray.OutboundInput(), bodyWriter)
|
||||||
|
|
||||||
|
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())
|
||||||
|
}()
|
||||||
|
|
||||||
|
responseMutex.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Command == protocol.RequestCommandUDP {
|
||||||
|
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)
|
||||||
|
|
||||||
|
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())
|
||||||
|
}()
|
||||||
|
|
||||||
|
responseMutex.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ShadowsocksAccount struct {
|
type ShadowsocksAccount struct {
|
||||||
Cipher Cipher
|
Cipher Cipher
|
||||||
Key []byte
|
Key []byte
|
||||||
|
OneTimeAuth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ShadowsocksAccount) Equals(another protocol.Account) bool {
|
func (this *ShadowsocksAccount) Equals(another protocol.Account) bool {
|
||||||
@ -43,8 +44,9 @@ func (this *Account) AsAccount() (protocol.Account, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &ShadowsocksAccount{
|
return &ShadowsocksAccount{
|
||||||
Cipher: cipher,
|
Cipher: cipher,
|
||||||
Key: this.GetCipherKey(),
|
Key: this.GetCipherKey(),
|
||||||
|
OneTimeAuth: this.Ota == Account_Auto || this.Ota == Account_Enabled,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,3 +342,46 @@ func DecodeUDPPacket(user *protocol.User, payload *alloc.Buffer) (*protocol.Requ
|
|||||||
|
|
||||||
return request, payload, nil
|
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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
5
proxy/shadowsocks/shadowsocks_test.go
Normal file
5
proxy/shadowsocks/shadowsocks_test.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package shadowsocks_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
// . "v2ray.com/core/proxy/shadowsocks"
|
||||||
|
)
|
@ -30,11 +30,9 @@ func TestShadowsocksServerConfigParsing(t *testing.T) {
|
|||||||
rawAccount, err := config.User.GetTypedAccount()
|
rawAccount, err := config.User.GetTypedAccount()
|
||||||
assert.Error(err).IsNil()
|
assert.Error(err).IsNil()
|
||||||
|
|
||||||
account, ok := rawAccount.(*shadowsocks.Account)
|
account, ok := rawAccount.(*shadowsocks.ShadowsocksAccount)
|
||||||
assert.Bool(ok).IsTrue()
|
assert.Bool(ok).IsTrue()
|
||||||
|
|
||||||
cipher, err := account.GetCipher()
|
assert.Int(account.Cipher.KeySize()).Equals(16)
|
||||||
assert.Error(err).IsNil()
|
assert.Bytes(account.Key).Equals([]byte{160, 224, 26, 2, 22, 110, 9, 80, 65, 52, 80, 20, 38, 243, 224, 241})
|
||||||
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})
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user