Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import (

// readBufferSize is the size used for bufio.Reader's internal buffer.
//
// For v1 the header length is at most 108 bytes.
// For v2 the header length is at most 52 bytes plus the length of the TLVs.
// We use 256 bytes to be safe.
// This is kept low to reduce per-connection memory overhead. If the header is
// larger than readBufferSize, the header will be decoded with multiple Read
// calls. For v1 the header length is at most 108 bytes. For v2 the header
// length is at most 52 bytes plus the length of the TLVs. We use 256 bytes to
// accommodate for the most common cases.
const readBufferSize = 256

var (
Expand Down
14 changes: 13 additions & 1 deletion v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import (
"net"
)

// maxV2HeaderSize is the maximum acceptable size of a V2 header.
//
// A V2 header may be at most 16 bytes + 64KiB large. We enforce a lower limit
// to mitigate memory allocation DoS while allowing real-world legitimate
// headers. PP2_SUBTYPE_SSL_CLIENT_CERT is typically between 1 and 2KiB, so we
// use a 4KiB limit to leave some room for other TLVs.
const maxV2HeaderSize = 4096

var (
lengthUnspec = uint16(0)
lengthV4 = uint16(12)
Expand Down Expand Up @@ -108,7 +116,7 @@ func parseVersion2(reader *bufio.Reader) (header *Header, err error) {
return header, nil
}

if _, err := reader.Peek(int(length)); err != nil {
if length > maxV2HeaderSize {
return nil, ErrInvalidLength
}

Expand Down Expand Up @@ -161,6 +169,10 @@ func parseVersion2(reader *bufio.Reader) (header *Header, err error) {
return nil, err
}

if payloadReader.N != 0 {
return nil, ErrInvalidLength
}

return header, nil
}

Expand Down
29 changes: 26 additions & 3 deletions v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ var (
fixtureIPv6V2TLV = fixtureWithTLV(lengthV6Bytes, fixtureIPv6Address, fixtureTLV)
fixtureUnspecTLV = fixtureWithTLV(lengthUnspecBytes, []byte{}, fixtureTLV)

fixtureMediumTLV = make([]byte, 2048)
fixtureV2MediumTLV = fixtureWithTLV(lengthV4Bytes, fixtureIPv4Address, fixtureMediumTLV)

fixtureTooLargeTLV = make([]byte, 10*1024)
fixtureV2TooLargeTLV = fixtureWithTLV(lengthV4Bytes, fixtureIPv4Address, fixtureTooLargeTLV)

// Arbitrary bytes following proxy bytes.
arbitraryTailBytes = []byte{'\x99', '\x97', '\x98'}
)
Expand Down Expand Up @@ -126,12 +132,12 @@ var invalidParseV2Tests = []struct {
{
desc: "TCPv4 with mismatching length",
reader: newBufioReader(append(append(SIGV2, byte(PROXY), byte(TCPv4)), lengthV4Bytes...)),
expectedError: ErrInvalidLength,
expectedError: ErrInvalidAddress,
},
{
desc: "TCPv6 with mismatching length",
reader: newBufioReader(append(append(SIGV2, byte(PROXY), byte(TCPv6)), lengthV6Bytes...)),
expectedError: ErrInvalidLength,
expectedError: ErrInvalidAddress,
},
{
desc: "TCPv4 length zero but with address and ports",
Expand All @@ -141,13 +147,18 @@ var invalidParseV2Tests = []struct {
{
desc: "TCPv6 with IPv6 length but IPv4 address and ports",
reader: newBufioReader(append(append(append(SIGV2, byte(PROXY), byte(TCPv6)), lengthV6Bytes...), fixtureIPv4Address...)),
expectedError: ErrInvalidLength,
expectedError: ErrInvalidAddress,
},
{
desc: "unspec length greater than zero but no TLVs",
reader: newBufioReader(append(append(SIGV2, byte(LOCAL), byte(UNSPEC)), fixtureUnspecTLV[:2]...)),
expectedError: ErrInvalidLength,
},
{
desc: "TCPv4 with too large TLV",
reader: newBufioReader(append(append(SIGV2, byte(PROXY), byte(TCPv4)), fixtureV2TooLargeTLV...)),
expectedError: ErrInvalidLength,
},
}

func TestParseV2Invalid(t *testing.T) {
Expand Down Expand Up @@ -289,6 +300,18 @@ var validParseAndWriteV2Tests = []struct {
DestinationAddr: unixDatagramAddr,
},
},
{
desc: "proxy TCPv4 with medium TLV",
reader: newBufioReader(append(append(SIGV2, byte(PROXY), byte(TCPv4)), fixtureV2MediumTLV...)),
expectedHeader: &Header{
Version: 2,
Command: PROXY,
TransportProtocol: TCPv4,
SourceAddr: v4addr,
DestinationAddr: v4addr,
rawTLVs: fixtureMediumTLV,
},
},
}

func TestParseV2Valid(t *testing.T) {
Expand Down
Loading