mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-05-22 21:39:40 +08:00
* fix crash in LengthFieldBasedFrameDecoder for malicious length values Motivation: LengthFieldBasedFrameDecoder will cause a fatal error if the length value does not fit into an `Int`. This can happen if `lengthFieldLength` is set to `.eight` and we are on a 64 bit platform or if `lengthFieldLength` is set to `.four` and we are on a 32-bit platform. If we then receive a length field value which is greater than `Int.max` the conversion from `UInt` to `Int` will cause a fatal error. This could be abused to crash a server by only sending 4 or 8 bytes. Modifications: safely convert UInt64 & UInt32 to Int and throw an error if they can't be represented as an Int Result: - LengthFieldBasedFrameDecoder with lengthFieldLength set to `.eight` can no longer crash the server on a 64-bit platform - LengthFieldBasedFrameDecoder with lengthFieldLength set to `.four` can no longer crash the server on a 32-bit platform * use early exit instead of XCTSkipIf * add support for `.eight` on 32-bit platforms * limit frame length to `Int32.max` * change test names * throw correct error * fix compilation for Swift 5.0 and add NIO prefix to error enum * add test for maximum allowed length and one above the maximum allowed length Signed-off-by: David Nadoba <dnadoba@gmail.com> * run XCTest script Signed-off-by: David Nadoba <dnadoba@gmail.com> Co-authored-by: Johannes Weiss <johannesweiss@apple.com>
571 lines
26 KiB
Swift
571 lines
26 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import XCTest
|
|
import NIO
|
|
@testable import NIOExtras
|
|
import NIOTestUtils
|
|
|
|
private let standardDataString = "abcde"
|
|
|
|
class LengthFieldBasedFrameDecoderTest: XCTestCase {
|
|
|
|
private var channel: EmbeddedChannel!
|
|
private var decoderUnderTest: ByteToMessageHandler<LengthFieldBasedFrameDecoder>!
|
|
|
|
override func setUp() {
|
|
self.channel = EmbeddedChannel()
|
|
}
|
|
func testReadUInt32From3Bytes() {
|
|
var buffer = ByteBuffer(bytes: [
|
|
0, 0, 5,
|
|
5, 0, 0,
|
|
])
|
|
XCTAssertEqual(buffer.read24UInt(endianness: .big), 5)
|
|
print(buffer.readableBytesView)
|
|
XCTAssertEqual(buffer.read24UInt(endianness: .little), 5)
|
|
}
|
|
func testReadAndWriteUInt32From3BytesBasicVerification() {
|
|
let inputs: [UInt32] = [
|
|
0,
|
|
1,
|
|
5,
|
|
UInt32(UInt8.max),
|
|
UInt32(UInt16.max),
|
|
UInt32(UInt16.max) << 8 &+ UInt32(UInt8.max),
|
|
UInt32(UInt8.max) - 1,
|
|
UInt32(UInt16.max) - 1,
|
|
UInt32(UInt16.max) << 8 &+ UInt32(UInt8.max) - 1,
|
|
UInt32(UInt8.max) + 1,
|
|
UInt32(UInt16.max) + 1,
|
|
]
|
|
|
|
for input in inputs {
|
|
var buffer = ByteBuffer()
|
|
buffer.write24UInt(input, endianness: .big)
|
|
XCTAssertEqual(buffer.read24UInt(endianness: .big), input)
|
|
|
|
buffer.write24UInt(input, endianness: .little)
|
|
XCTAssertEqual(buffer.read24UInt(endianness: .little), input)
|
|
}
|
|
}
|
|
func testDecodeWithUInt8HeaderWithData() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .one,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataBytes: [UInt8] = [10, 20, 30, 40]
|
|
let dataBytesLengthHeader = UInt8(dataBytes.count)
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 5)
|
|
buffer.writeBytes([dataBytesLengthHeader])
|
|
buffer.writeBytes(dataBytes)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(dataBytes,
|
|
try self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView.map {
|
|
$0
|
|
}))
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithUInt16HeaderWithString() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .two,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength: UInt16 = 5
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 7) // 2 byte header + 5 character string
|
|
buffer.writeInteger(dataLength, endianness: .little, as: UInt16.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithUInt24HeaderWithString() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldBitLength: .threeBytes,
|
|
lengthFieldEndianness: .big))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 8) // 3 byte header + 5 character string
|
|
buffer.writeBytes([0, 0, 5])
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithUInt32HeaderWithString() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .four,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength: UInt32 = 5
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 9) // 4 byte header + 5 character string
|
|
buffer.writeInteger(dataLength, endianness: .little, as: UInt32.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithUInt64HeaderWithString() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .eight,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength: UInt64 = 5
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 13) // 8 byte header + 5 character string
|
|
buffer.writeInteger(dataLength, endianness: .little, as: UInt64.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithInt64HeaderWithString() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .eight,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength: Int64 = 5
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 13) // 8 byte header + 5 character string
|
|
buffer.writeInteger(dataLength, endianness: .little, as: Int64.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithInt64HeaderStringBigEndian() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .eight,
|
|
lengthFieldEndianness: .big))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength: Int64 = 5
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 13) // 8 byte header + 5 character string
|
|
buffer.writeInteger(dataLength, endianness: .big, as: Int64.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithInt64HeaderStringDefaultingToBigEndian() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .eight))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength: Int64 = 5
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 13) // 8 byte header + 5 character string
|
|
buffer.writeInteger(dataLength, endianness: .big, as: Int64.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithUInt8HeaderTwoFrames() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .one,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let firstFrameDataLength: UInt8 = 5
|
|
let secondFrameDataLength: UInt8 = 3
|
|
let secondFrameString = "123"
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 10) // 1 byte header + 5 character string + 1 byte header + 3 character string
|
|
buffer.writeInteger(firstFrameDataLength, endianness: .little, as: UInt8.self)
|
|
buffer.writeString(standardDataString)
|
|
buffer.writeInteger(secondFrameDataLength, endianness: .little, as: UInt8.self)
|
|
buffer.writeString(secondFrameString)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(secondFrameString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithUInt8HeaderFrameSplitIncomingData() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .two,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let frameDataLength: UInt16 = 5
|
|
|
|
// Write and try to read both bytes of the data individually
|
|
let frameDataLengthFirstByte: UInt8 = UInt8(frameDataLength)
|
|
let frameDataLengthSecondByte: UInt8 = 0
|
|
|
|
var firstBuffer = self.channel.allocator.buffer(capacity: 1) // Byte 1 of 2 byte header header
|
|
firstBuffer.writeInteger(frameDataLengthFirstByte, endianness: .little, as: UInt8.self)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(firstBuffer).isEmpty)
|
|
|
|
// Read should fail because there is not yet enough data.
|
|
XCTAssertNoThrow(XCTAssertNil(try self.channel.readInbound()))
|
|
|
|
var secondBuffer = self.channel.allocator.buffer(capacity: 1) // Byte 2 of 2 byte header header
|
|
secondBuffer.writeInteger(frameDataLengthSecondByte, endianness: .little, as: UInt8.self)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(secondBuffer).isEmpty)
|
|
|
|
// Read should fail because there is not yet enough data.
|
|
XCTAssertNoThrow(XCTAssertNil(try self.channel.readInbound()))
|
|
|
|
// Write and try to read each byte of the data individually
|
|
for (index, character) in standardDataString.enumerated() {
|
|
|
|
var characterBuffer = self.channel.allocator.buffer(capacity: 1)
|
|
characterBuffer.writeString(String(character))
|
|
|
|
if index < standardDataString.count - 1 {
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(characterBuffer).isEmpty)
|
|
// Read should fail because there is not yet enough data.
|
|
XCTAssertNoThrow(XCTAssertNil(try self.channel.readInbound()))
|
|
} else {
|
|
XCTAssertTrue(try self.channel.writeInbound(characterBuffer).isFull)
|
|
}
|
|
}
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testEmptyBuffer() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .one,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let buffer = self.channel.allocator.buffer(capacity: 1)
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isEmpty)
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testDecodeWithUInt16HeaderWithPartialHeader() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .two,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength: UInt8 = 5 // 8 byte is only half the length required
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 7) // 2 byte header + 5 character string
|
|
buffer.writeInteger(dataLength, endianness: .little, as: UInt8.self)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isEmpty)
|
|
XCTAssertThrowsError(try channel.finish()) { error in
|
|
if let error = error as? NIOExtrasErrors.LeftOverBytesError {
|
|
XCTAssertEqual(1 /* just the one byte of the length that arrived */, error.leftOverBytes.readableBytes)
|
|
} else {
|
|
XCTFail("unexpected error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testDecodeWithUInt16HeaderWithPartialBody() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .two,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength: UInt16 = 7
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 9) // 2 byte header + 7 character string
|
|
buffer.writeInteger(dataLength, endianness: .little, as: UInt16.self)
|
|
buffer.writeString(standardDataString) // 2 bytes short of the 7 required.
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isEmpty)
|
|
XCTAssertThrowsError(try channel.finish()) { error in
|
|
if let error = error as? NIOExtrasErrors.LeftOverBytesError {
|
|
XCTAssertEqual(Int(dataLength) - 2 /* we're 2 bytes short of the required 7 */,
|
|
error.leftOverBytes.readableBytes)
|
|
} else {
|
|
XCTFail("unexpected error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testRemoveHandlerWhenBufferIsEmpty() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .eight,
|
|
lengthFieldEndianness: .little))
|
|
try? self.channel.pipeline.addHandler(self.decoderUnderTest).wait()
|
|
|
|
let dataLength: Int64 = 5
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 13) // 8 byte header + 5 character string
|
|
buffer.writeInteger(dataLength, endianness: .little, as: Int64.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertTrue(try self.channel.writeInbound(buffer).isFull)
|
|
|
|
let removeFuture = self.channel.pipeline.removeHandler(self.decoderUnderTest)
|
|
(channel.eventLoop as! EmbeddedEventLoop).run()
|
|
XCTAssertNoThrow(try removeFuture.wait())
|
|
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
XCTAssertNoThrow(try self.channel.throwIfErrorCaught())
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testRemoveHandlerWhenBufferIsNotEmpty() throws {
|
|
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .eight,
|
|
lengthFieldEndianness: .little))
|
|
try? self.channel.pipeline.addHandler(self.decoderUnderTest).wait()
|
|
|
|
let extraUnusedDataString = "fghi"
|
|
let dataLength: Int64 = 5
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 17) // 8 byte header + 5 character string + 4 unused
|
|
buffer.writeInteger(dataLength, endianness: .little, as: Int64.self)
|
|
buffer.writeString(standardDataString + extraUnusedDataString)
|
|
|
|
XCTAssertTrue(try channel.writeInbound(buffer).isFull)
|
|
|
|
let removeFuture = self.channel.pipeline.removeHandler(self.decoderUnderTest)
|
|
(channel.eventLoop as! EmbeddedEventLoop).run()
|
|
XCTAssertNoThrow(try removeFuture.wait())
|
|
|
|
XCTAssertThrowsError(try self.channel.throwIfErrorCaught()) { error in
|
|
guard let error = error as? NIOExtrasErrors.LeftOverBytesError else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
var expectedBuffer = self.channel.allocator.buffer(capacity: 7)
|
|
expectedBuffer.writeString(extraUnusedDataString)
|
|
XCTAssertEqual(error.leftOverBytes, expectedBuffer)
|
|
}
|
|
|
|
XCTAssertNoThrow(XCTAssertEqual(standardDataString,
|
|
try (self.channel.readInbound(as: ByteBuffer.self)?.readableBytesView).map {
|
|
String(decoding: $0, as: Unicode.UTF8.self)
|
|
}))
|
|
XCTAssertTrue(try self.channel.finish().isClean)
|
|
}
|
|
|
|
func testCloseInChannelRead() {
|
|
let channel = EmbeddedChannel(handler: ByteToMessageHandler(LengthFieldBasedFrameDecoder(lengthFieldLength: .four)))
|
|
class CloseInReadHandler: ChannelInboundHandler {
|
|
typealias InboundIn = ByteBuffer
|
|
|
|
private var numberOfReads = 0
|
|
|
|
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
|
self.numberOfReads += 1
|
|
XCTAssertEqual(1, self.numberOfReads)
|
|
XCTAssertEqual([UInt8(100)], Array(self.unwrapInboundIn(data).readableBytesView))
|
|
context.close().whenFailure { error in
|
|
XCTFail("unexpected error: \(error)")
|
|
}
|
|
context.fireChannelRead(data)
|
|
}
|
|
}
|
|
XCTAssertNoThrow(try channel.pipeline.addHandler(CloseInReadHandler()).wait())
|
|
|
|
var buf = channel.allocator.buffer(capacity: 1024)
|
|
buf.writeBytes([UInt8(0), 0, 0, 1, 100])
|
|
XCTAssertNoThrow(try channel.writeInbound(buf))
|
|
XCTAssertNoThrow(XCTAssertEqual([100], Array((try channel.readInbound() as ByteBuffer?)!.readableBytesView)))
|
|
XCTAssertNoThrow(XCTAssertNil(try channel.readInbound()))
|
|
}
|
|
|
|
func testBasicVerification() {
|
|
let inputs: [(NIOLengthFieldBitLength, [(Int, String)])] = [
|
|
(.oneByte, [
|
|
(6, "abcdef"),
|
|
(0, ""),
|
|
(9, "123456789"),
|
|
(Int(UInt8.max),
|
|
String(decoding: Array(repeating: UInt8(ascii: "X"), count: Int(UInt8.max)), as: Unicode.UTF8.self)),
|
|
]),
|
|
(.twoBytes, [
|
|
(1, "a"),
|
|
(0, ""),
|
|
(9, "123456789"),
|
|
(307,
|
|
String(decoding: Array(repeating: UInt8(ascii: "X"), count: 307), as: Unicode.UTF8.self)),
|
|
]),
|
|
(.threeBytes, [
|
|
(1, "a"),
|
|
(0, ""),
|
|
(9, "123456789"),
|
|
(307,
|
|
String(decoding: Array(repeating: UInt8(ascii: "X"), count: 307), as: Unicode.UTF8.self)),
|
|
]),
|
|
(.fourBytes, [
|
|
(1, "a"),
|
|
(0, ""),
|
|
(3, "333"),
|
|
(307,
|
|
String(decoding: Array(repeating: UInt8(ascii: "X"), count: 307), as: Unicode.UTF8.self)),
|
|
]),
|
|
(.eightBytes, [
|
|
(1, "a"),
|
|
(0, ""),
|
|
(4, "aaaa"),
|
|
(307,
|
|
String(decoding: Array(repeating: UInt8(ascii: "X"), count: 307), as: Unicode.UTF8.self)),
|
|
]),
|
|
]
|
|
|
|
for input in inputs {
|
|
let (lenBytes, inputData) = input
|
|
|
|
func byteBuffer(length: Int, string: String) -> ByteBuffer {
|
|
var buf = self.channel.allocator.buffer(capacity: string.utf8.count + 8)
|
|
buf.writeInteger(length)
|
|
buf.moveReaderIndex(forwardBy: 8 - lenBytes.length)
|
|
buf.writeString(string)
|
|
return buf
|
|
}
|
|
|
|
let inputOutputPairs = inputData.map { (input: (Int, String)) -> (ByteBuffer, [ByteBuffer]) in
|
|
let bytes = byteBuffer(length: input.0, string: input.1)
|
|
return (bytes, [bytes.getSlice(at: bytes.readerIndex + lenBytes.length, length: input.0)!])
|
|
}
|
|
XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(inputOutputPairs: inputOutputPairs) {
|
|
LengthFieldBasedFrameDecoder(lengthFieldBitLength: lenBytes)
|
|
})
|
|
}
|
|
}
|
|
func testMaximumAllowedLengthWith32BitFieldLength() throws {
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .four,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength = UInt32(Int32.max)
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 4) // 4 byte header
|
|
buffer.writeInteger(dataLength, endianness: .little, as: UInt32.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertNoThrow(try self.channel.writeInbound(buffer))
|
|
}
|
|
|
|
func testMaliciousLengthWith32BitFieldLength() throws {
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .four,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength = UInt32(Int32.max) + 1
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 4) // 4 byte header
|
|
buffer.writeInteger(dataLength, endianness: .little, as: UInt32.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertThrowsError(try self.channel.writeInbound(buffer))
|
|
}
|
|
|
|
func testMaximumAllowedLengthWith64BitFieldLength() throws {
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .eight,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength = UInt64(Int32.max)
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 8) // 8 byte header
|
|
buffer.writeInteger(dataLength, endianness: .little, as: UInt64.self)
|
|
buffer.writeString(standardDataString)
|
|
|
|
XCTAssertNoThrow(try self.channel.writeInbound(buffer))
|
|
}
|
|
|
|
func testMaliciousLengthWith64BitFieldLength() {
|
|
self.decoderUnderTest = .init(LengthFieldBasedFrameDecoder(lengthFieldLength: .eight,
|
|
lengthFieldEndianness: .little))
|
|
XCTAssertNoThrow(try self.channel.pipeline.addHandler(self.decoderUnderTest).wait())
|
|
|
|
let dataLength = UInt64(Int32.max) + 1
|
|
|
|
var buffer = self.channel.allocator.buffer(capacity: 8) // 8 byte header
|
|
buffer.writeInteger(dataLength, endianness: .little, as: UInt64.self)
|
|
|
|
XCTAssertThrowsError(try self.channel.writeInbound(buffer))
|
|
}
|
|
}
|