[822] | 1 | package proxyproto
|
---|
| 2 |
|
---|
| 3 | import (
|
---|
| 4 | "bufio"
|
---|
| 5 | "bytes"
|
---|
| 6 | "fmt"
|
---|
| 7 | "net"
|
---|
| 8 | "net/netip"
|
---|
| 9 | "strconv"
|
---|
| 10 | "strings"
|
---|
| 11 | )
|
---|
| 12 |
|
---|
| 13 | const (
|
---|
| 14 | crlf = "\r\n"
|
---|
| 15 | separator = " "
|
---|
| 16 | )
|
---|
| 17 |
|
---|
| 18 | func initVersion1() *Header {
|
---|
| 19 | header := new(Header)
|
---|
| 20 | header.Version = 1
|
---|
| 21 | // Command doesn't exist in v1
|
---|
| 22 | header.Command = PROXY
|
---|
| 23 | return header
|
---|
| 24 | }
|
---|
| 25 |
|
---|
| 26 | func parseVersion1(reader *bufio.Reader) (*Header, error) {
|
---|
| 27 | //The header cannot be more than 107 bytes long. Per spec:
|
---|
| 28 | //
|
---|
| 29 | // (...)
|
---|
| 30 | // - worst case (optional fields set to 0xff) :
|
---|
| 31 | // "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
|
---|
| 32 | // => 5 + 1 + 7 + 1 + 39 + 1 + 39 + 1 + 5 + 1 + 5 + 2 = 107 chars
|
---|
| 33 | //
|
---|
| 34 | // So a 108-byte buffer is always enough to store all the line and a
|
---|
| 35 | // trailing zero for string processing.
|
---|
| 36 | //
|
---|
| 37 | // It must also be CRLF terminated, as above. The header does not otherwise
|
---|
| 38 | // contain a CR or LF byte.
|
---|
| 39 | //
|
---|
| 40 | // ISSUE #69
|
---|
| 41 | // We can't use Peek here as it will block trying to fill the buffer, which
|
---|
| 42 | // will never happen if the header is TCP4 or TCP6 (max. 56 and 104 bytes
|
---|
| 43 | // respectively) and the server is expected to speak first.
|
---|
| 44 | //
|
---|
| 45 | // Similarly, we can't use ReadString or ReadBytes as these will keep reading
|
---|
| 46 | // until the delimiter is found; an abusive client could easily disrupt a
|
---|
| 47 | // server by sending a large amount of data that do not contain a LF byte.
|
---|
| 48 | // Another means of attack would be to start connections and simply not send
|
---|
| 49 | // data after the initial PROXY signature bytes, accumulating a large
|
---|
| 50 | // number of blocked goroutines on the server. ReadSlice will also block for
|
---|
| 51 | // a delimiter when the internal buffer does not fill up.
|
---|
| 52 | //
|
---|
| 53 | // A plain Read is also problematic since we risk reading past the end of the
|
---|
| 54 | // header without being able to easily put the excess bytes back into the reader's
|
---|
| 55 | // buffer (with the current implementation's design).
|
---|
| 56 | //
|
---|
| 57 | // So we use a ReadByte loop, which solves the overflow problem and avoids
|
---|
| 58 | // reading beyond the end of the header. However, we need one more trick to harden
|
---|
| 59 | // against partial header attacks (slow loris) - per spec:
|
---|
| 60 | //
|
---|
| 61 | // (..) The sender must always ensure that the header is sent at once, so that
|
---|
| 62 | // the transport layer maintains atomicity along the path to the receiver. The
|
---|
| 63 | // receiver may be tolerant to partial headers or may simply drop the connection
|
---|
| 64 | // when receiving a partial header. Recommendation is to be tolerant, but
|
---|
| 65 | // implementation constraints may not always easily permit this.
|
---|
| 66 | //
|
---|
| 67 | // We are subject to such implementation constraints. So we return an error if
|
---|
| 68 | // the header cannot be fully extracted with a single read of the underlying
|
---|
| 69 | // reader.
|
---|
| 70 | buf := make([]byte, 0, 107)
|
---|
| 71 | for {
|
---|
| 72 | b, err := reader.ReadByte()
|
---|
| 73 | if err != nil {
|
---|
| 74 | return nil, fmt.Errorf(ErrCantReadVersion1Header.Error()+": %v", err)
|
---|
| 75 | }
|
---|
| 76 | buf = append(buf, b)
|
---|
| 77 | if b == '\n' {
|
---|
| 78 | // End of header found
|
---|
| 79 | break
|
---|
| 80 | }
|
---|
| 81 | if len(buf) == 107 {
|
---|
| 82 | // No delimiter in first 107 bytes
|
---|
| 83 | return nil, ErrVersion1HeaderTooLong
|
---|
| 84 | }
|
---|
| 85 | if reader.Buffered() == 0 {
|
---|
| 86 | // Header was not buffered in a single read. Since we can't
|
---|
| 87 | // differentiate between genuine slow writers and DoS agents,
|
---|
| 88 | // we abort. On healthy networks, this should never happen.
|
---|
| 89 | return nil, ErrCantReadVersion1Header
|
---|
| 90 | }
|
---|
| 91 | }
|
---|
| 92 |
|
---|
| 93 | // Check for CR before LF.
|
---|
| 94 | if len(buf) < 2 || buf[len(buf)-2] != '\r' {
|
---|
| 95 | return nil, ErrLineMustEndWithCrlf
|
---|
| 96 | }
|
---|
| 97 |
|
---|
| 98 | // Check full signature.
|
---|
| 99 | tokens := strings.Split(string(buf[:len(buf)-2]), separator)
|
---|
| 100 |
|
---|
| 101 | // Expect at least 2 tokens: "PROXY" and the transport protocol.
|
---|
| 102 | if len(tokens) < 2 {
|
---|
| 103 | return nil, ErrCantReadAddressFamilyAndProtocol
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | // Read address family and protocol
|
---|
| 107 | var transportProtocol AddressFamilyAndProtocol
|
---|
| 108 | switch tokens[1] {
|
---|
| 109 | case "TCP4":
|
---|
| 110 | transportProtocol = TCPv4
|
---|
| 111 | case "TCP6":
|
---|
| 112 | transportProtocol = TCPv6
|
---|
| 113 | case "UNKNOWN":
|
---|
| 114 | transportProtocol = UNSPEC // doesn't exist in v1 but fits UNKNOWN
|
---|
| 115 | default:
|
---|
| 116 | return nil, ErrCantReadAddressFamilyAndProtocol
|
---|
| 117 | }
|
---|
| 118 |
|
---|
| 119 | // Expect 6 tokens only when UNKNOWN is not present.
|
---|
| 120 | if transportProtocol != UNSPEC && len(tokens) < 6 {
|
---|
| 121 | return nil, ErrCantReadAddressFamilyAndProtocol
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | // When a signature is found, allocate a v1 header with Command set to PROXY.
|
---|
| 125 | // Command doesn't exist in v1 but set it for other parts of this library
|
---|
| 126 | // to rely on it for determining connection details.
|
---|
| 127 | header := initVersion1()
|
---|
| 128 |
|
---|
| 129 | // Transport protocol has been processed already.
|
---|
| 130 | header.TransportProtocol = transportProtocol
|
---|
| 131 |
|
---|
| 132 | // When UNKNOWN, set the command to LOCAL and return early
|
---|
| 133 | if header.TransportProtocol == UNSPEC {
|
---|
| 134 | header.Command = LOCAL
|
---|
| 135 | return header, nil
|
---|
| 136 | }
|
---|
| 137 |
|
---|
| 138 | // Otherwise, continue to read addresses and ports
|
---|
| 139 | sourceIP, err := parseV1IPAddress(header.TransportProtocol, tokens[2])
|
---|
| 140 | if err != nil {
|
---|
| 141 | return nil, err
|
---|
| 142 | }
|
---|
| 143 | destIP, err := parseV1IPAddress(header.TransportProtocol, tokens[3])
|
---|
| 144 | if err != nil {
|
---|
| 145 | return nil, err
|
---|
| 146 | }
|
---|
| 147 | sourcePort, err := parseV1PortNumber(tokens[4])
|
---|
| 148 | if err != nil {
|
---|
| 149 | return nil, err
|
---|
| 150 | }
|
---|
| 151 | destPort, err := parseV1PortNumber(tokens[5])
|
---|
| 152 | if err != nil {
|
---|
| 153 | return nil, err
|
---|
| 154 | }
|
---|
| 155 | header.SourceAddr = &net.TCPAddr{
|
---|
| 156 | IP: sourceIP,
|
---|
| 157 | Port: sourcePort,
|
---|
| 158 | }
|
---|
| 159 | header.DestinationAddr = &net.TCPAddr{
|
---|
| 160 | IP: destIP,
|
---|
| 161 | Port: destPort,
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | return header, nil
|
---|
| 165 | }
|
---|
| 166 |
|
---|
| 167 | func (header *Header) formatVersion1() ([]byte, error) {
|
---|
| 168 | // As of version 1, only "TCP4" ( \x54 \x43 \x50 \x34 ) for TCP over IPv4,
|
---|
| 169 | // and "TCP6" ( \x54 \x43 \x50 \x36 ) for TCP over IPv6 are allowed.
|
---|
| 170 | var proto string
|
---|
| 171 | switch header.TransportProtocol {
|
---|
| 172 | case TCPv4:
|
---|
| 173 | proto = "TCP4"
|
---|
| 174 | case TCPv6:
|
---|
| 175 | proto = "TCP6"
|
---|
| 176 | default:
|
---|
| 177 | // Unknown connection (short form)
|
---|
| 178 | return []byte("PROXY UNKNOWN" + crlf), nil
|
---|
| 179 | }
|
---|
| 180 |
|
---|
| 181 | sourceAddr, sourceOK := header.SourceAddr.(*net.TCPAddr)
|
---|
| 182 | destAddr, destOK := header.DestinationAddr.(*net.TCPAddr)
|
---|
| 183 | if !sourceOK || !destOK {
|
---|
| 184 | return nil, ErrInvalidAddress
|
---|
| 185 | }
|
---|
| 186 |
|
---|
| 187 | sourceIP, destIP := sourceAddr.IP, destAddr.IP
|
---|
| 188 | switch header.TransportProtocol {
|
---|
| 189 | case TCPv4:
|
---|
| 190 | sourceIP = sourceIP.To4()
|
---|
| 191 | destIP = destIP.To4()
|
---|
| 192 | case TCPv6:
|
---|
| 193 | sourceIP = sourceIP.To16()
|
---|
| 194 | destIP = destIP.To16()
|
---|
| 195 | }
|
---|
| 196 | if sourceIP == nil || destIP == nil {
|
---|
| 197 | return nil, ErrInvalidAddress
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 | buf := bytes.NewBuffer(make([]byte, 0, 108))
|
---|
| 201 | buf.Write(SIGV1)
|
---|
| 202 | buf.WriteString(separator)
|
---|
| 203 | buf.WriteString(proto)
|
---|
| 204 | buf.WriteString(separator)
|
---|
| 205 | buf.WriteString(sourceIP.String())
|
---|
| 206 | buf.WriteString(separator)
|
---|
| 207 | buf.WriteString(destIP.String())
|
---|
| 208 | buf.WriteString(separator)
|
---|
| 209 | buf.WriteString(strconv.Itoa(sourceAddr.Port))
|
---|
| 210 | buf.WriteString(separator)
|
---|
| 211 | buf.WriteString(strconv.Itoa(destAddr.Port))
|
---|
| 212 | buf.WriteString(crlf)
|
---|
| 213 |
|
---|
| 214 | return buf.Bytes(), nil
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | func parseV1PortNumber(portStr string) (int, error) {
|
---|
| 218 | port, err := strconv.Atoi(portStr)
|
---|
| 219 | if err != nil || port < 0 || port > 65535 {
|
---|
| 220 | return 0, ErrInvalidPortNumber
|
---|
| 221 | }
|
---|
| 222 | return port, nil
|
---|
| 223 | }
|
---|
| 224 |
|
---|
| 225 | func parseV1IPAddress(protocol AddressFamilyAndProtocol, addrStr string) (net.IP, error) {
|
---|
| 226 | addr, err := netip.ParseAddr(addrStr)
|
---|
| 227 | if err != nil {
|
---|
| 228 | return nil, ErrInvalidAddress
|
---|
| 229 | }
|
---|
| 230 |
|
---|
| 231 | switch protocol {
|
---|
| 232 | case TCPv4:
|
---|
| 233 | if addr.Is4() {
|
---|
| 234 | return net.IP(addr.AsSlice()), nil
|
---|
| 235 | }
|
---|
| 236 | case TCPv6:
|
---|
| 237 | if addr.Is6() || addr.Is4In6() {
|
---|
| 238 | return net.IP(addr.AsSlice()), nil
|
---|
| 239 | }
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | return nil, ErrInvalidAddress
|
---|
| 243 | }
|
---|