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