Selaa lähdekoodia

shadowsocks client

Darien Raymond 9 vuotta sitten
vanhempi
commit
c221802963

+ 92 - 2
proxy/shadowsocks/client.go

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

+ 6 - 4
proxy/shadowsocks/config.go

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

+ 43 - 0
proxy/shadowsocks/protocol.go

@@ -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 - 0
proxy/shadowsocks/shadowsocks_test.go

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

+ 3 - 5
tools/conf/shadowsocks_test.go

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