Skip to content

Parsing v2 UNSPEC and v1 PROXY UNKNOWN not implemented #60

@bohanwood

Description

@bohanwood

Hi! I found that this library is missing the ability to parse these two kinds of header, which are required by the protocol and mandatory to implement, while trying to resolve XTLS/Xray-core#166

v2 UNSPEC

When this header is received:

0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a 20 00 00 00

which is + LOCAL command (\x20) + UNSPEC address family UNSPEC protocol (\x00)
ErrUnsupportedAddressFamilyAndProtocol is returned

go-proxyproto/v2.go

Lines 85 to 87 in adbbabe

if _, ok := supportedTransportProtocol[header.TransportProtocol]; !ok {
return nil, ErrUnsupportedAddressFamilyAndProtocol
}

This is because UNSPEC is absent in supportedTransportProtocol
const (
UNSPEC AddressFamilyAndProtocol = '\x00'
TCPv4 AddressFamilyAndProtocol = '\x11'
UDPv4 AddressFamilyAndProtocol = '\x12'
TCPv6 AddressFamilyAndProtocol = '\x21'
UDPv6 AddressFamilyAndProtocol = '\x22'
UnixStream AddressFamilyAndProtocol = '\x31'
UnixDatagram AddressFamilyAndProtocol = '\x32'
)
var supportedTransportProtocol = map[AddressFamilyAndProtocol]bool{
TCPv4: true,
UDPv4: true,
TCPv6: true,
UDPv6: true,
UnixStream: true,
UnixDatagram: true,
}

This UNSPEC header is used when HAProxy doing health checks, or the proxied client is requesting via a unix socket.
Also the protocol spec says:

Only the UNSPEC protocol byte (\x00) is mandatory to implement on the receiver.
A receiver is not required to implement other ones, provided that it
automatically falls back to the UNSPEC mode for the valid combinations above
that it does not support.

v1 PROXY UNKNOWN

When header PROXY UNKNOWN\r\n is received,

go-proxyproto/v1.go

Lines 25 to 33 in adbbabe

// Make sure we have a v1 header
line, err := reader.ReadString('\n')
if !strings.HasSuffix(line, crlf) {
return nil, ErrCantReadProtocolVersionAndCommand
}
tokens := strings.Split(line[:len(line)-2], separator)
if len(tokens) < 6 {
return nil, ErrCantReadProtocolVersionAndCommand
}

returns ErrCantReadProtocolVersionAndCommand.
This is because in this case, there's only 2 parts separated by spaces, instead of 6 parts as PROXY <PROTO> <SRC_ADDR> <DEST_ADDR> <SRC_PORT> <DEST_PORT>
The protocol spec says:

If the announced transport protocol is "UNKNOWN", then the receiver knows that
the sender speaks the correct PROXY protocol with the appropriate version, and
SHOULD accept the connection and use the real connection's parameters as if
there were no PROXY protocol header on the wire.

Also, this kind of header can be formatted by the library.

go-proxyproto/v1.go

Lines 76 to 88 in adbbabe

func (header *Header) formatVersion1() ([]byte, error) {
// As of version 1, only "TCP4" ( \x54 \x43 \x50 \x34 ) for TCP over IPv4,
// and "TCP6" ( \x54 \x43 \x50 \x36 ) for TCP over IPv6 are allowed.
var proto string
switch header.TransportProtocol {
case TCPv4:
proto = "TCP4"
case TCPv6:
proto = "TCP6"
default:
// Unknown connection (short form)
return []byte("PROXY UNKNOWN\r\n"), nil
}

Although I really don't know much about Golang, I'll try my best to support you to resolve this issue. Thanks!

Metadata

Metadata

Assignees

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions