|
|
@@ -16,6 +16,7 @@ import (
|
|
|
v2net "github.com/v2ray/v2ray-core/common/net"
|
|
|
"github.com/v2ray/v2ray-core/proxy"
|
|
|
"github.com/v2ray/v2ray-core/proxy/internal"
|
|
|
+ vmessio "github.com/v2ray/v2ray-core/proxy/vmess/io"
|
|
|
"github.com/v2ray/v2ray-core/proxy/vmess/protocol"
|
|
|
"github.com/v2ray/v2ray-core/transport/ray"
|
|
|
)
|
|
|
@@ -38,6 +39,9 @@ func (this *VMessOutboundHandler) Dispatch(firstPacket v2net.Packet, ray ray.Out
|
|
|
Address: firstPacket.Destination().Address(),
|
|
|
Port: firstPacket.Destination().Port(),
|
|
|
}
|
|
|
+ if command == protocol.CmdUDP {
|
|
|
+ request.Option |= protocol.OptionChunk
|
|
|
+ }
|
|
|
|
|
|
buffer := alloc.NewSmallBuffer()
|
|
|
defer buffer.Release() // Buffer is released after communication finishes.
|
|
|
@@ -83,7 +87,7 @@ func (this *VMessOutboundHandler) startCommunicate(request *protocol.VMessReques
|
|
|
responseFinish.Lock()
|
|
|
|
|
|
go this.handleRequest(conn, request, firstPacket, input, &requestFinish)
|
|
|
- go this.handleResponse(conn, request, dest, output, &responseFinish, (request.Command == protocol.CmdUDP))
|
|
|
+ go this.handleResponse(conn, request, dest, output, &responseFinish)
|
|
|
|
|
|
requestFinish.Lock()
|
|
|
conn.CloseWrite()
|
|
|
@@ -121,6 +125,10 @@ func (this *VMessOutboundHandler) handleRequest(conn net.Conn, request *protocol
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ if request.IsChunkStream() {
|
|
|
+ vmessio.Authenticate(firstChunk)
|
|
|
+ }
|
|
|
+
|
|
|
aesStream.XORKeyStream(firstChunk.Value, firstChunk.Value)
|
|
|
buffer.Append(firstChunk.Value)
|
|
|
firstChunk.Release()
|
|
|
@@ -132,7 +140,12 @@ func (this *VMessOutboundHandler) handleRequest(conn net.Conn, request *protocol
|
|
|
}
|
|
|
|
|
|
if moreChunks {
|
|
|
- v2io.ChanToWriter(encryptRequestWriter, input)
|
|
|
+ var streamWriter v2io.Writer
|
|
|
+ streamWriter = v2io.NewAdaptiveWriter(encryptRequestWriter)
|
|
|
+ if request.IsChunkStream() {
|
|
|
+ streamWriter = vmessio.NewAuthChunkWriter(streamWriter)
|
|
|
+ }
|
|
|
+ v2io.ChanToWriter(streamWriter, input)
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
@@ -141,7 +154,7 @@ func headerMatch(request *protocol.VMessRequest, responseHeader byte) bool {
|
|
|
return request.ResponseHeader == responseHeader
|
|
|
}
|
|
|
|
|
|
-func (this *VMessOutboundHandler) handleResponse(conn net.Conn, request *protocol.VMessRequest, dest v2net.Destination, output chan<- *alloc.Buffer, finish *sync.Mutex, isUDP bool) {
|
|
|
+func (this *VMessOutboundHandler) handleResponse(conn net.Conn, request *protocol.VMessRequest, dest v2net.Destination, output chan<- *alloc.Buffer, finish *sync.Mutex) {
|
|
|
defer finish.Unlock()
|
|
|
defer close(output)
|
|
|
responseKey := md5.Sum(request.RequestKey[:])
|
|
|
@@ -154,39 +167,40 @@ func (this *VMessOutboundHandler) handleResponse(conn net.Conn, request *protoco
|
|
|
}
|
|
|
decryptResponseReader := v2crypto.NewCryptionReader(aesStream, conn)
|
|
|
|
|
|
- buffer, err := v2io.ReadFrom(decryptResponseReader, nil)
|
|
|
+ buffer := alloc.NewSmallBuffer()
|
|
|
+ defer buffer.Release()
|
|
|
+ _, err = io.ReadFull(decryptResponseReader, buffer.Value[:4])
|
|
|
+
|
|
|
if err != nil {
|
|
|
log.Error("VMessOut: Failed to read VMess response (", buffer.Len(), " bytes): ", err)
|
|
|
- buffer.Release()
|
|
|
return
|
|
|
}
|
|
|
- if buffer.Len() < 4 || !headerMatch(request, buffer.Value[0]) {
|
|
|
+ if !headerMatch(request, buffer.Value[0]) {
|
|
|
log.Warning("VMessOut: unexepcted response header. The connection is probably hijacked.")
|
|
|
return
|
|
|
}
|
|
|
- log.Info("VMessOut received ", buffer.Len()-4, " bytes from ", conn.RemoteAddr())
|
|
|
|
|
|
- responseBegin := 4
|
|
|
if buffer.Value[2] != 0 {
|
|
|
+ command := buffer.Value[2]
|
|
|
dataLen := int(buffer.Value[3])
|
|
|
- if buffer.Len() < dataLen+4 { // Rare case
|
|
|
- diffBuffer := make([]byte, dataLen+4-buffer.Len())
|
|
|
- io.ReadFull(decryptResponseReader, diffBuffer)
|
|
|
- buffer.Append(diffBuffer)
|
|
|
+ _, err := io.ReadFull(decryptResponseReader, buffer.Value[:dataLen])
|
|
|
+ if err != nil {
|
|
|
+ log.Error("VMessOut: Failed to read response command: ", err)
|
|
|
+ return
|
|
|
}
|
|
|
- command := buffer.Value[2]
|
|
|
- data := buffer.Value[4 : 4+dataLen]
|
|
|
+ data := buffer.Value[:dataLen]
|
|
|
go this.handleCommand(dest, command, data)
|
|
|
- responseBegin = 4 + dataLen
|
|
|
}
|
|
|
|
|
|
- buffer.SliceFrom(responseBegin)
|
|
|
- output <- buffer
|
|
|
-
|
|
|
- if !isUDP {
|
|
|
- v2io.RawReaderToChan(output, decryptResponseReader)
|
|
|
+ var reader v2io.Reader
|
|
|
+ if request.IsChunkStream() {
|
|
|
+ reader = vmessio.NewAuthChunkReader(decryptResponseReader)
|
|
|
+ } else {
|
|
|
+ reader = v2io.NewAdaptiveReader(decryptResponseReader)
|
|
|
}
|
|
|
|
|
|
+ v2io.ReaderToChan(output, reader)
|
|
|
+
|
|
|
return
|
|
|
}
|
|
|
|