Пример #1
0
func (this *AuthenticationWriter) Write(payload *alloc.Buffer) error {
	defer payload.Release()

	this.Authenticator.Seal(payload)
	_, err := this.Writer.Write(payload.Value)
	return err
}
Пример #2
0
// Write implements Writer.Write().
func (this *AdaptiveWriter) Write(buffer *alloc.Buffer) error {
	nBytes, err := this.writer.Write(buffer.Value)
	if nBytes < buffer.Len() {
		_, err = this.writer.Write(buffer.Value[nBytes:])
	}
	return err
}
Пример #3
0
func (this *SimpleAuthenticator) Open(buffer *alloc.Buffer) bool {
	len := buffer.Len()
	xtra := 4 - len%4
	if xtra != 0 {
		buffer.Slice(0, len+xtra)
	}
	xorbkd(buffer.Value)
	if xtra != 0 {
		buffer.Slice(0, len)
	}

	fnvHash := fnv.New32a()
	fnvHash.Write(buffer.Value[4:])
	if serial.BytesToUint32(buffer.Value[:4]) != fnvHash.Sum32() {
		return false
	}

	length := serial.BytesToUint16(buffer.Value[4:6])
	if buffer.Len()-6 != int(length) {
		return false
	}

	buffer.SliceFrom(6)

	return true
}
Пример #4
0
func (this *OutboundConnectionHandler) Dispatch(destination v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error {
	input := ray.OutboundInput()
	output := ray.OutboundOutput()

	this.Destination = destination
	this.ConnOutput.Write(payload.Value)
	payload.Release()

	writeFinish := &sync.Mutex{}

	writeFinish.Lock()

	go func() {
		v2writer := v2io.NewAdaptiveWriter(this.ConnOutput)
		defer v2writer.Release()

		v2io.Pipe(input, v2writer)
		writeFinish.Unlock()
		input.Release()
	}()

	writeFinish.Lock()

	v2reader := v2io.NewAdaptiveReader(this.ConnInput)
	defer v2reader.Release()

	v2io.Pipe(v2reader, output)
	output.Close()

	return nil
}
Пример #5
0
func ReadFrom(reader io.Reader, buffer *alloc.Buffer) (*alloc.Buffer, error) {
	if buffer == nil {
		buffer = alloc.NewBuffer()
	}
	nBytes, err := reader.Read(buffer.Value)
	buffer.Slice(0, nBytes)
	return buffer, err
}
Пример #6
0
func (this *DokodemoDoor) handleUDPResponse(dest v2net.Destination, payload *alloc.Buffer) {
	defer payload.Release()
	this.udpMutex.RLock()
	defer this.udpMutex.RUnlock()
	if !this.accepting {
		return
	}
	this.udpHub.WriteTo(payload.Value, dest)
}
Пример #7
0
func (this *BlackHole) Dispatch(destination v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error {
	payload.Release()

	this.response.WriteTo(ray.OutboundOutput())
	ray.OutboundOutput().Close()

	ray.OutboundInput().Release()

	return nil
}
Пример #8
0
func (this *SocksServer) handleUDPPayload(payload *alloc.Buffer, source v2net.Destination) {
	log.Info("Socks: Client UDP connection from ", source)
	request, err := protocol.ReadUDPRequest(payload.Value)
	payload.Release()

	if err != nil {
		log.Error("Socks: Failed to parse UDP request: ", err)
		return
	}
	if request.Data.Len() == 0 {
		request.Data.Release()
		return
	}
	if request.Fragment != 0 {
		log.Warning("Socks: Dropping fragmented UDP packets.")
		// TODO handle fragments
		request.Data.Release()
		return
	}

	udpPacket := v2net.NewPacket(request.Destination(), request.Data, false)
	log.Info("Socks: Send packet to ", udpPacket.Destination(), " with ", request.Data.Len(), " bytes")
	this.udpServer.Dispatch(source, udpPacket, func(packet v2net.Packet) {
		response := &protocol.Socks5UDPRequest{
			Fragment: 0,
			Address:  udpPacket.Destination().Address(),
			Port:     udpPacket.Destination().Port(),
			Data:     packet.Chunk(),
		}
		log.Info("Socks: Writing back UDP response with ", response.Data.Len(), " bytes to ", packet.Destination())

		udpMessage := alloc.NewSmallBuffer().Clear()
		response.Write(udpMessage)

		this.udpMutex.RLock()
		if !this.accepting {
			this.udpMutex.RUnlock()
			return
		}
		nBytes, err := this.udpHub.WriteTo(udpMessage.Value, packet.Destination())
		this.udpMutex.RUnlock()
		udpMessage.Release()
		response.Data.Release()
		if err != nil {
			log.Error("Socks: failed to write UDP message (", nBytes, " bytes) to ", packet.Destination(), ": ", err)
		}
	})
}
Пример #9
0
func (this *Listener) OnReceive(payload *alloc.Buffer, src v2net.Destination) {
	defer payload.Release()

	if valid := this.block.Open(payload); !valid {
		log.Info("KCP|Listener: discarding invalid payload from ", src)
		return
	}
	if !this.running {
		return
	}
	this.Lock()
	defer this.Unlock()
	if !this.running {
		return
	}
	if payload.Len() < 4 {
		return
	}
	conv := serial.BytesToUint16(payload.Value)
	cmd := Command(payload.Value[2])
	sourceId := src.NetAddr() + "|" + serial.Uint16ToString(conv)
	conn, found := this.sessions[sourceId]
	if !found {
		if cmd == CommandTerminate {
			return
		}
		log.Debug("KCP|Listener: Creating session with id(", sourceId, ") from ", src)
		writer := &Writer{
			id:       sourceId,
			hub:      this.hub,
			dest:     src,
			listener: this,
		}
		srcAddr := &net.UDPAddr{
			IP:   src.Address().IP(),
			Port: int(src.Port()),
		}
		conn = NewConnection(conv, writer, this.localAddr, srcAddr, this.block)
		select {
		case this.awaitingConns <- conn:
		case <-time.After(time.Second * 5):
			conn.Close()
			return
		}
		this.sessions[sourceId] = conn
	}
	conn.Input(payload.Value)
}
Пример #10
0
func Authenticate(buffer *alloc.Buffer) {
	fnvHash := fnv.New32a()
	fnvHash.Write(buffer.Value)
	buffer.PrependHash(fnvHash)

	buffer.PrependUint16(uint16(buffer.Len()))
}
Пример #11
0
func Authenticate(buffer *alloc.Buffer) {
	fnvHash := fnv.New32a()
	fnvHash.Write(buffer.Value)

	buffer.SliceBack(4)
	fnvHash.Sum(buffer.Value[:0])

	buffer.Prepend(serial.Uint16Literal(uint16(buffer.Len())).Bytes())
}
Пример #12
0
// Write implements Writer.Write(). Write() takes ownership of the given buffer.
func (this *AdaptiveWriter) Write(buffer *alloc.Buffer) error {
	defer buffer.Release()
	for !buffer.IsEmpty() {
		nBytes, err := this.writer.Write(buffer.Value)
		if err != nil {
			return err
		}
		buffer.SliceFrom(nBytes)
	}
	return nil
}
Пример #13
0
func (this *VMessInboundHandler) generateCommand(buffer *alloc.Buffer) {
	cmd := byte(0)
	commandBytes := alloc.NewSmallBuffer().Clear()
	defer commandBytes.Release()

	if this.features != nil && this.features.Detour != nil {

		tag := this.features.Detour.ToTag
		if this.space.HasInboundHandlerManager() {
			handlerManager := this.space.InboundHandlerManager()
			handler, availableMin := handlerManager.GetHandler(tag)
			inboundHandler, ok := handler.(*VMessInboundHandler)
			if ok {
				if availableMin > 255 {
					availableMin = 255
				}
				cmd = byte(1)
				log.Info("VMessIn: Pick detour handler for port ", inboundHandler.Port(), " for ", availableMin, " minutes.")
				user := inboundHandler.GetUser()
				saCmd := &command.SwitchAccount{
					Port:     inboundHandler.Port(),
					ID:       user.ID.UUID(),
					AlterIds: serial.Uint16Literal(len(user.AlterIDs)),
					Level:    user.Level,
					ValidMin: byte(availableMin),
				}
				saCmd.Marshal(commandBytes)
			}
		}
	}

	if cmd == 0 || commandBytes.Len()+4 > 256 {
		buffer.AppendBytes(byte(0), byte(0))
	} else {
		buffer.AppendBytes(cmd, byte(commandBytes.Len()+4))
		fnv1hash := fnv.New32a()
		fnv1hash.Write(commandBytes.Value)
		hashValue := fnv1hash.Sum32()
		buffer.AppendBytes(byte(hashValue>>24), byte(hashValue>>16), byte(hashValue>>8), byte(hashValue))
		buffer.Append(commandBytes.Value)
	}
}
Пример #14
0
func (this *VMessInboundHandler) generateCommand(buffer *alloc.Buffer) {
	cmd := byte(0)
	commandBytes := alloc.NewSmallBuffer().Clear()
	defer commandBytes.Release()

	if this.features != nil && this.features.Detour != nil {
		tag := this.features.Detour.ToTag
		if this.space.HasInboundHandlerManager() {
			handlerManager := this.space.InboundHandlerManager()
			handler, availableSec := handlerManager.GetHandler(tag)
			inboundHandler, ok := handler.(*VMessInboundHandler)
			if ok {
				user := inboundHandler.GetUser()
				availableMin := availableSec / 60
				if availableMin > 255 {
					availableMin = 255
				}

				saCmd := &command.SwitchAccount{
					Port:     inboundHandler.Port(),
					ID:       user.ID.UUID(),
					AlterIds: serial.Uint16Literal(len(user.AlterIDs)),
					Level:    user.Level,
					ValidMin: byte(availableMin),
				}
				saCmd.Marshal(commandBytes)
			}
		}
	}

	if commandBytes.Len() > 256 {
		buffer.AppendBytes(byte(0), byte(0))
	} else {
		buffer.AppendBytes(cmd, byte(commandBytes.Len()))
		buffer.Append(commandBytes.Value)
	}
}
Пример #15
0
// ToBytes returns a VMessRequest in the form of byte array.
func (request *VMessRequest) ToBytes(idHash user.CounterHash, randomRangeInt64 user.RandomInt64InRange, buffer *alloc.Buffer) (*alloc.Buffer, error) {
	if buffer == nil {
		buffer = alloc.NewSmallBuffer().Clear()
	}

	counter := randomRangeInt64(time.Now().UTC().Unix(), 30)
	hash := idHash.Hash(request.UserId.Bytes[:], counter)

	buffer.Append(hash)

	encryptionBegin := buffer.Len()

	buffer.AppendBytes(request.Version)
	buffer.Append(request.RequestIV)
	buffer.Append(request.RequestKey)
	buffer.Append(request.ResponseHeader)
	buffer.AppendBytes(request.Command)
	buffer.Append(request.Address.PortBytes())

	switch {
	case request.Address.IsIPv4():
		buffer.AppendBytes(addrTypeIPv4)
		buffer.Append(request.Address.IP())
	case request.Address.IsIPv6():
		buffer.AppendBytes(addrTypeIPv6)
		buffer.Append(request.Address.IP())
	case request.Address.IsDomain():
		buffer.AppendBytes(addrTypeDomain, byte(len(request.Address.Domain())))
		buffer.Append([]byte(request.Address.Domain()))
	}

	encryptionEnd := buffer.Len()

	fnv1a := fnv.New32a()
	fnv1a.Write(buffer.Value[encryptionBegin:encryptionEnd])

	fnvHash := fnv1a.Sum32()
	buffer.AppendBytes(byte(fnvHash>>24), byte(fnvHash>>16), byte(fnvHash>>8), byte(fnvHash))
	encryptionEnd += 4

	aesCipher, err := aes.NewCipher(request.UserId.CmdKey())
	if err != nil {
		return nil, err
	}
	aesStream := cipher.NewCFBEncrypter(aesCipher, user.Int64Hash(counter))
	aesStream.XORKeyStream(buffer.Value[encryptionBegin:encryptionEnd], buffer.Value[encryptionBegin:encryptionEnd])

	return buffer, nil
}
Пример #16
0
func (this *SimpleAuthenticator) Seal(buffer *alloc.Buffer) {
	buffer.PrependUint16(uint16(buffer.Len()))
	fnvHash := fnv.New32a()
	fnvHash.Write(buffer.Value)
	buffer.PrependHash(fnvHash)

	len := buffer.Len()
	xtra := 4 - len%4
	if xtra != 0 {
		buffer.Slice(0, len+xtra)
	}
	xorfwd(buffer.Value)
	if xtra != 0 {
		buffer.Slice(0, len)
	}
}
Пример #17
0
func (this *Shadowsocks) handlerUDPPayload(payload *alloc.Buffer, source v2net.Destination) {
	defer payload.Release()

	iv := payload.Value[:this.config.Cipher.IVSize()]
	key := this.config.Key
	payload.SliceFrom(this.config.Cipher.IVSize())

	reader, err := this.config.Cipher.NewDecodingStream(key, iv, payload)
	if err != nil {
		log.Error("Shadowsocks: Failed to create decoding stream: ", err)
		return
	}

	request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(key, iv)), true)
	if err != nil {
		log.Access(source, serial.StringLiteral(""), log.AccessRejected, serial.StringLiteral(err.Error()))
		log.Warning("Shadowsocks: Invalid request from ", source, ": ", err)
		return
	}

	dest := v2net.UDPDestination(request.Address, request.Port)
	log.Access(source, dest, log.AccessAccepted, serial.StringLiteral(""))
	log.Info("Shadowsocks: Tunnelling request to ", dest)

	packet := v2net.NewPacket(dest, request.UDPPayload, false)
	this.udpServer.Dispatch(source, packet, func(packet v2net.Packet) {
		defer packet.Chunk().Release()

		response := alloc.NewBuffer().Slice(0, this.config.Cipher.IVSize())
		defer response.Release()

		rand.Read(response.Value)
		respIv := response.Value

		writer, err := this.config.Cipher.NewEncodingStream(key, respIv, response)
		if err != nil {
			log.Error("Shadowsocks: Failed to create encoding stream: ", err)
			return
		}

		switch {
		case request.Address.IsIPv4():
			writer.Write([]byte{AddrTypeIPv4})
			writer.Write(request.Address.IP())
		case request.Address.IsIPv6():
			writer.Write([]byte{AddrTypeIPv6})
			writer.Write(request.Address.IP())
		case request.Address.IsDomain():
			writer.Write([]byte{AddrTypeDomain, byte(len(request.Address.Domain()))})
			writer.Write([]byte(request.Address.Domain()))
		}

		writer.Write(request.Port.Bytes())
		writer.Write(packet.Chunk().Value)

		if request.OTA {
			respAuth := NewAuthenticator(HeaderKeyGenerator(key, respIv))
			respAuth.Authenticate(response.Value, response.Value[this.config.Cipher.IVSize():])
		}

		this.udpHub.WriteTo(response.Value, source)
	})
}
Пример #18
0
func (request *Socks5UDPRequest) Write(buffer *alloc.Buffer) {
	buffer.AppendBytes(0, 0, request.Fragment)
	switch {
	case request.Address.IsIPv4():
		buffer.AppendBytes(AddrTypeIPv4).Append(request.Address.IP())
	case request.Address.IsIPv6():
		buffer.AppendBytes(AddrTypeIPv6).Append(request.Address.IP())
	case request.Address.IsDomain():
		buffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain()))).Append([]byte(request.Address.Domain()))
	}
	buffer.Append(request.Port.Bytes())
	buffer.Append(request.Data.Value)
}
Пример #19
0
func (r *Socks4AuthenticationResponse) Write(buffer *alloc.Buffer) {
	buffer.AppendBytes(
		byte(0x00), r.result, byte(r.port>>8), byte(r.port),
		r.ip[0], r.ip[1], r.ip[2], r.ip[3])
}
Пример #20
0
func (r *Socks5Response) Write(buffer *alloc.Buffer) {
	buffer.AppendBytes(r.Version, r.Error, 0x00 /* reserved */, r.AddrType)
	switch r.AddrType {
	case 0x01:
		buffer.Append(r.IPv4[:])
	case 0x03:
		buffer.AppendBytes(byte(len(r.Domain)))
		buffer.Append([]byte(r.Domain))
	case 0x04:
		buffer.Append(r.IPv6[:])
	}
	buffer.AppendBytes(byte(r.Port>>8), byte(r.Port))
}
Пример #21
0
// ToBytes returns a VMessRequest in the form of byte array.
func (this *VMessRequest) ToBytes(timestampGenerator RandomTimestampGenerator, buffer *alloc.Buffer) (*alloc.Buffer, error) {
	if buffer == nil {
		buffer = alloc.NewSmallBuffer().Clear()
	}

	timestamp := timestampGenerator.Next()
	idHash := IDHash(this.User.AnyValidID().Bytes())
	idHash.Write(timestamp.Bytes())

	hashStart := buffer.Len()
	buffer.Slice(0, hashStart+16)
	idHash.Sum(buffer.Value[hashStart:hashStart])

	encryptionBegin := buffer.Len()

	buffer.AppendBytes(this.Version)
	buffer.Append(this.RequestIV)
	buffer.Append(this.RequestKey)
	buffer.AppendBytes(this.ResponseHeader, this.Option, byte(0), byte(0))
	buffer.AppendBytes(this.Command)
	buffer.Append(this.Port.Bytes())

	switch {
	case this.Address.IsIPv4():
		buffer.AppendBytes(addrTypeIPv4)
		buffer.Append(this.Address.IP())
	case this.Address.IsIPv6():
		buffer.AppendBytes(addrTypeIPv6)
		buffer.Append(this.Address.IP())
	case this.Address.IsDomain():
		buffer.AppendBytes(addrTypeDomain, byte(len(this.Address.Domain())))
		buffer.Append([]byte(this.Address.Domain()))
	}

	encryptionEnd := buffer.Len()

	fnv1a := fnv.New32a()
	fnv1a.Write(buffer.Value[encryptionBegin:encryptionEnd])

	fnvHash := fnv1a.Sum32()
	buffer.AppendBytes(byte(fnvHash>>24), byte(fnvHash>>16), byte(fnvHash>>8), byte(fnvHash))
	encryptionEnd += 4

	timestampHash := md5.New()
	timestampHash.Write(timestamp.HashBytes())
	iv := timestampHash.Sum(nil)
	aesStream, err := v2crypto.NewAesEncryptionStream(this.User.ID.CmdKey(), iv)
	if err != nil {
		return nil, err
	}
	aesStream.XORKeyStream(buffer.Value[encryptionBegin:encryptionEnd], buffer.Value[encryptionBegin:encryptionEnd])

	return buffer, nil
}
Пример #22
0
func (this *AuthChunkReader) Read() (*alloc.Buffer, error) {
	var buffer *alloc.Buffer
	if this.last != nil {
		buffer = this.last
		this.last = nil
	} else {
		buffer = alloc.NewBufferWithSize(4096).Clear()
	}

	if this.chunkLength == -1 {
		for buffer.Len() < 6 {
			_, err := buffer.FillFrom(this.reader)
			if err != nil {
				buffer.Release()
				return nil, io.ErrUnexpectedEOF
			}
		}
		length := serial.BytesToUint16(buffer.Value[:2])
		this.chunkLength = int(length) - 4
		this.validator = NewValidator(serial.BytesToUint32(buffer.Value[2:6]))
		buffer.SliceFrom(6)
		if buffer.Len() < this.chunkLength && this.chunkLength <= 2048 {
			_, err := buffer.FillFrom(this.reader)
			if err != nil {
				buffer.Release()
				return nil, io.ErrUnexpectedEOF
			}
		}
	} else if buffer.Len() < this.chunkLength {
		_, err := buffer.FillFrom(this.reader)
		if err != nil {
			buffer.Release()
			return nil, io.ErrUnexpectedEOF
		}
	}

	if this.chunkLength == 0 {
		buffer.Release()
		return nil, io.EOF
	}

	if buffer.Len() < this.chunkLength {
		this.validator.Consume(buffer.Value)
		this.chunkLength -= buffer.Len()
	} else {
		this.validator.Consume(buffer.Value[:this.chunkLength])
		if !this.validator.Validate() {
			buffer.Release()
			return nil, transport.ErrCorruptedPacket
		}
		leftLength := buffer.Len() - this.chunkLength
		if leftLength > 0 {
			this.last = alloc.NewBufferWithSize(leftLength + 4096).Clear()
			this.last.Append(buffer.Value[this.chunkLength:])
			buffer.Slice(0, this.chunkLength)
		}

		this.chunkLength = -1
		this.validator = nil
	}

	return buffer, nil
}
Пример #23
0
func (this *Server) handlerUDPPayload(payload *alloc.Buffer, source v2net.Destination) {
	defer payload.Release()

	ivLen := this.config.Cipher.IVSize()
	iv := payload.Value[:ivLen]
	key := this.config.Key
	payload.SliceFrom(ivLen)

	stream, err := this.config.Cipher.NewDecodingStream(key, iv)
	if err != nil {
		log.Error("Shadowsocks: Failed to create decoding stream: ", err)
		return
	}

	reader := crypto.NewCryptionReader(stream, payload)

	request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(key, iv)), true)
	if err != nil {
		if err != io.EOF {
			log.Access(source, "", log.AccessRejected, err)
			log.Warning("Shadowsocks: Invalid request from ", source, ": ", err)
		}
		return
	}
	//defer request.Release()

	dest := v2net.UDPDestination(request.Address, request.Port)
	log.Access(source, dest, log.AccessAccepted, "")
	log.Info("Shadowsocks: Tunnelling request to ", dest)

	this.udpServer.Dispatch(source, dest, request.DetachUDPPayload(), func(destination v2net.Destination, payload *alloc.Buffer) {
		defer payload.Release()

		response := alloc.NewBuffer().Slice(0, ivLen)
		defer response.Release()

		rand.Read(response.Value)
		respIv := response.Value

		stream, err := this.config.Cipher.NewEncodingStream(key, respIv)
		if err != nil {
			log.Error("Shadowsocks: Failed to create encoding stream: ", err)
			return
		}

		writer := crypto.NewCryptionWriter(stream, response)

		switch {
		case request.Address.IsIPv4():
			writer.Write([]byte{AddrTypeIPv4})
			writer.Write(request.Address.IP())
		case request.Address.IsIPv6():
			writer.Write([]byte{AddrTypeIPv6})
			writer.Write(request.Address.IP())
		case request.Address.IsDomain():
			writer.Write([]byte{AddrTypeDomain, byte(len(request.Address.Domain()))})
			writer.Write([]byte(request.Address.Domain()))
		}

		writer.Write(request.Port.Bytes())
		writer.Write(payload.Value)

		if request.OTA {
			respAuth := NewAuthenticator(HeaderKeyGenerator(key, respIv))
			respAuth.Authenticate(response.Value, response.Value[ivLen:])
		}

		this.udpHub.WriteTo(response.Value, source)
	})
}
Пример #24
0
func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error {
	log.Info("Freedom: Opening connection to ", destination)

	defer payload.Release()
	defer ray.OutboundInput().Release()
	defer ray.OutboundOutput().Close()

	var conn internet.Connection
	if this.domainStrategy == DomainStrategyUseIP && destination.Address().IsDomain() {
		destination = this.ResolveIP(destination)
	}
	err := retry.Timed(5, 100).On(func() error {
		rawConn, err := internet.Dial(this.meta.Address, destination, this.meta.StreamSettings)
		if err != nil {
			return err
		}
		conn = rawConn
		return nil
	})
	if err != nil {
		log.Warning("Freedom: Failed to open connection to ", destination, ": ", err)
		return err
	}
	defer conn.Close()

	input := ray.OutboundInput()
	output := ray.OutboundOutput()
	var readMutex, writeMutex sync.Mutex
	readMutex.Lock()
	writeMutex.Lock()

	conn.Write(payload.Value)

	go func() {
		v2writer := v2io.NewAdaptiveWriter(conn)
		defer v2writer.Release()

		v2io.Pipe(input, v2writer)
		writeMutex.Unlock()
	}()

	go func() {
		defer readMutex.Unlock()

		var reader io.Reader = conn

		timeout := this.timeout
		if destination.IsUDP() {
			timeout = 16
		}
		if timeout > 0 {
			reader = v2net.NewTimeOutReader(int(timeout) /* seconds */, conn)
		}

		v2reader := v2io.NewAdaptiveReader(reader)
		defer v2reader.Release()

		v2io.Pipe(v2reader, output)
		ray.OutboundOutput().Close()
	}()

	writeMutex.Lock()
	if tcpConn, ok := conn.(*tcp.RawConnection); ok {
		tcpConn.CloseWrite()
	}
	readMutex.Lock()

	return nil
}