// Package proxyproto implements Proxy Protocol (v1 and v2) parser and writer, as per specification: // https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt
package proxyproto import ( ) var ( // SIGV1 is the signature for PROXY protocol v1. SIGV1 = []byte{'\x50', '\x52', '\x4F', '\x58', '\x59'} // SIGV2 is the signature for PROXY protocol v2. SIGV2 = []byte{'\x0D', '\x0A', '\x0D', '\x0A', '\x00', '\x0D', '\x0A', '\x51', '\x55', '\x49', '\x54', '\x0A'} // ErrCantReadVersion1Header indicates a v1 header could not be read. ErrCantReadVersion1Header = errors.New("proxyproto: can't read version 1 header") // ErrVersion1HeaderTooLong indicates a v1 header is too long. ErrVersion1HeaderTooLong = errors.New("proxyproto: version 1 header must be 107 bytes or less") // ErrLineMustEndWithCrlf indicates a v1 header is invalid, must end with \r\n. ErrLineMustEndWithCrlf = errors.New("proxyproto: version 1 header is invalid, must end with \\r\\n") // ErrCantReadProtocolVersionAndCommand indicates a protocol version and command could not be read. ErrCantReadProtocolVersionAndCommand = errors.New("proxyproto: can't read proxy protocol version and command") // ErrCantReadAddressFamilyAndProtocol indicates an address family and protocol could not be read. ErrCantReadAddressFamilyAndProtocol = errors.New("proxyproto: can't read address family or protocol") // ErrCantReadLength indicates a length could not be read. ErrCantReadLength = errors.New("proxyproto: can't read length") // ErrCantResolveSourceUnixAddress indicates a source Unix address could not be resolved. ErrCantResolveSourceUnixAddress = errors.New("proxyproto: can't resolve source Unix address") // ErrCantResolveDestinationUnixAddress indicates a destination Unix address could not be resolved. ErrCantResolveDestinationUnixAddress = errors.New("proxyproto: can't resolve destination Unix address") // ErrNoProxyProtocol indicates a proxy protocol signature is not present. ErrNoProxyProtocol = errors.New("proxyproto: proxy protocol signature not present") // ErrUnknownProxyProtocolVersion indicates an unknown proxy protocol version. ErrUnknownProxyProtocolVersion = errors.New("proxyproto: unknown proxy protocol version") // ErrUnsupportedProtocolVersionAndCommand indicates an unsupported protocol version and command. ErrUnsupportedProtocolVersionAndCommand = errors.New("proxyproto: unsupported proxy protocol version and command") // ErrUnsupportedAddressFamilyAndProtocol indicates an unsupported address family and protocol. ErrUnsupportedAddressFamilyAndProtocol = errors.New("proxyproto: unsupported address family and protocol") // ErrInvalidLength indicates an invalid length. ErrInvalidLength = errors.New("proxyproto: invalid length") // ErrInvalidAddress indicates an invalid address. ErrInvalidAddress = errors.New("proxyproto: invalid address") // ErrInvalidPortNumber indicates an invalid port number. ErrInvalidPortNumber = errors.New("proxyproto: invalid port number") // ErrSuperfluousProxyHeader indicates an upstream connection sent a PROXY header but isn't allowed to send one. ErrSuperfluousProxyHeader = errors.New("proxyproto: upstream connection sent PROXY header but isn't allowed to send one") ) // Header is the placeholder for proxy protocol header. type Header struct { Version byte Command ProtocolVersionAndCommand TransportProtocol AddressFamilyAndProtocol SourceAddr net.Addr DestinationAddr net.Addr rawTLVs []byte } // HeaderProxyFromAddrs creates a new PROXY header from a source and a // destination address. If version is zero, the latest protocol version is // used. // // The header is filled on a best-effort basis: if hints cannot be inferred // from the provided addresses, the header will be left unspecified. func ( byte, , net.Addr) *Header { if < 1 || > 2 { = 2 } := &Header{ Version: , Command: LOCAL, TransportProtocol: UNSPEC, } switch sourceAddr := .(type) { case *net.TCPAddr: if , := .(*net.TCPAddr); ! { break } if len(.IP.To4()) == net.IPv4len { .TransportProtocol = TCPv4 } else if len(.IP) == net.IPv6len { .TransportProtocol = TCPv6 } case *net.UDPAddr: if , := .(*net.UDPAddr); ! { break } if len(.IP.To4()) == net.IPv4len { .TransportProtocol = UDPv4 } else if len(.IP) == net.IPv6len { .TransportProtocol = UDPv6 } case *net.UnixAddr: if , := .(*net.UnixAddr); ! { break } switch .Net { case "unix": .TransportProtocol = UnixStream case "unixgram": .TransportProtocol = UnixDatagram } } if .TransportProtocol != UNSPEC { .Command = PROXY .SourceAddr = .DestinationAddr = } return } // TCPAddrs returns TCP source/destination addresses if the header is stream-based. func ( *Header) () (, *net.TCPAddr, bool) { if !.TransportProtocol.IsStream() { return nil, nil, false } , := .SourceAddr.(*net.TCPAddr) , := .DestinationAddr.(*net.TCPAddr) return , , && } // UDPAddrs returns UDP source/destination addresses if the header is datagram-based. func ( *Header) () (, *net.UDPAddr, bool) { if !.TransportProtocol.IsDatagram() { return nil, nil, false } , := .SourceAddr.(*net.UDPAddr) , := .DestinationAddr.(*net.UDPAddr) return , , && } // UnixAddrs returns UNIX source/destination addresses if the header is UNIX-based. func ( *Header) () (, *net.UnixAddr, bool) { if !.TransportProtocol.IsUnix() { return nil, nil, false } , := .SourceAddr.(*net.UnixAddr) , := .DestinationAddr.(*net.UnixAddr) return , , && } // IPs returns source/destination IPs for TCP/UDP headers. func ( *Header) () (, net.IP, bool) { if , , := .TCPAddrs(); { return .IP, .IP, true } if , , := .UDPAddrs(); { return .IP, .IP, true } return nil, nil, false } // Ports returns source/destination ports for TCP/UDP headers. func ( *Header) () (, int, bool) { if , , := .TCPAddrs(); { return .Port, .Port, true } if , , := .UDPAddrs(); { return .Port, .Port, true } return 0, 0, false } // EqualTo returns true if headers are equivalent, false otherwise. // Deprecated: use EqualsTo instead. This method will eventually be removed. func ( *Header) ( *Header) bool { return .EqualsTo() } // EqualsTo returns true if headers are equivalent, false otherwise. func ( *Header) ( *Header) bool { if == nil { return false } if .Version != .Version || .Command != .Command || .TransportProtocol != .TransportProtocol { return false } // TLVs only exist for version 2 if .Version == 2 && !bytes.Equal(.rawTLVs, .rawTLVs) { return false } // Return early for header with LOCAL command, which contains no address information if .Command == LOCAL { return true } return .SourceAddr.String() == .SourceAddr.String() && .DestinationAddr.String() == .DestinationAddr.String() } // WriteTo renders a proxy protocol header in a format and writes it to an io.Writer. func ( *Header) ( io.Writer) (int64, error) { , := .Format() if != nil { return 0, } return bytes.NewBuffer().WriteTo() } // Format renders a proxy protocol header in a format to write over the wire. func ( *Header) () ([]byte, error) { switch .Version { case 1: return .formatVersion1() case 2: return .formatVersion2() default: return nil, ErrUnknownProxyProtocolVersion } } // TLVs returns the TLVs stored into this header, if they exist. TLVs are optional for v2 of the protocol. func ( *Header) () ([]TLV, error) { return SplitTLVs(.rawTLVs) } // SetTLVs sets the TLVs stored in this header. This method replaces any // previous TLV. func ( *Header) ( []TLV) error { , := JoinTLVs() if != nil { return } .rawTLVs = return nil } // Read identifies the proxy protocol version and reads the remaining of // the header, accordingly. // // If proxy protocol header signature is not present, the reader buffer remains untouched // and is safe for reading outside of this code. // // If proxy protocol header signature is present but an error is raised while processing // the remaining header, assume the reader buffer to be in a corrupt state. // Also, this operation will block until enough bytes are available for peeking. func ( *bufio.Reader) (*Header, error) { // In order to improve speed for small non-PROXYed packets, take a peek at the first byte alone. , := .Peek(1) if != nil { if == io.EOF { return nil, ErrNoProxyProtocol } return nil, } if bytes.Equal([:1], SIGV1[:1]) || bytes.Equal([:1], SIGV2[:1]) { , := .Peek(5) if != nil { if == io.EOF { return nil, ErrNoProxyProtocol } return nil, } if bytes.Equal([:5], SIGV1) { return parseVersion1() } , = .Peek(12) if != nil { if == io.EOF { return nil, ErrNoProxyProtocol } return nil, } if bytes.Equal([:12], SIGV2) { return parseVersion2() } } return nil, ErrNoProxyProtocol } // ReadTimeout acts as Read but takes a timeout. If that timeout is reached, it's assumed // there's no proxy protocol header. func ( *bufio.Reader, time.Duration) (*Header, error) { type struct { *Header error } := make(chan *, 1) go func() { := &{} ., . = Read() <- }() := time.NewTimer() select { case := <-: .Stop() return ., . case <-.C: return nil, ErrNoProxyProtocol } }