mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-21 09:36:34 -05:00
shadowsocks client
This commit is contained in:
parent
be4f3d0772
commit
c221802963
@ -1,7 +1,11 @@
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"sync"
|
||||
"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"
|
||||
@ -39,10 +43,96 @@ func (this *Client) Dispatch(destination v2net.Destination, payload *alloc.Buffe
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Shadowsocks|Client: Failed to find an available destination:", err)
|
||||
return err
|
||||
return errors.New("Shadowsocks|Client: Failed to find an available destination:" + err.Error())
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -11,8 +11,9 @@ import (
|
||||
)
|
||||
|
||||
type ShadowsocksAccount struct {
|
||||
Cipher Cipher
|
||||
Key []byte
|
||||
Cipher Cipher
|
||||
Key []byte
|
||||
OneTimeAuth bool
|
||||
}
|
||||
|
||||
func (this *ShadowsocksAccount) Equals(another protocol.Account) bool {
|
||||
@ -43,8 +44,9 @@ func (this *Account) AsAccount() (protocol.Account, error) {
|
||||
return nil, err
|
||||
}
|
||||
return &ShadowsocksAccount{
|
||||
Cipher: cipher,
|
||||
Key: this.GetCipherKey(),
|
||||
Cipher: cipher,
|
||||
Key: this.GetCipherKey(),
|
||||
OneTimeAuth: this.Ota == Account_Auto || this.Ota == Account_Enabled,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -342,3 +342,46 @@ func DecodeUDPPacket(user *protocol.User, payload *alloc.Buffer) (*protocol.Requ
|
||||
|
||||
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()
|
||||
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})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user