@@ -3,6 +3,7 @@ package proxyproto
33import (
44 "bufio"
55 "bytes"
6+ "fmt"
67 "net"
78 "strconv"
89 "strings"
@@ -22,17 +23,79 @@ func initVersion1() *Header {
2223}
2324
2425func parseVersion1 (reader * bufio.Reader ) (* Header , error ) {
25- // Read until LF shows up, otherwise fail.
26- // At this point, can't be sure CR precedes LF which will be validated next.
27- line , err := reader .ReadString ('\n' )
28- if err != nil {
29- return nil , ErrLineMustEndWithCrlf
30- }
31- if ! strings .HasSuffix (line , crlf ) {
26+ //The header cannot be more than 107 bytes long. Per spec:
27+ //
28+ // (...)
29+ // - worst case (optional fields set to 0xff) :
30+ // "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
31+ // => 5 + 1 + 7 + 1 + 39 + 1 + 39 + 1 + 5 + 1 + 5 + 2 = 107 chars
32+ //
33+ // So a 108-byte buffer is always enough to store all the line and a
34+ // trailing zero for string processing.
35+ //
36+ // It must also be CRLF terminated, as above. The header does not otherwise
37+ // contain a CR or LF byte.
38+ //
39+ // ISSUE #69
40+ // We can't use Peek here as it will block trying to fill the buffer, which
41+ // will never happen if the header is TCP4 or TCP6 (max. 56 and 104 bytes
42+ // respectively) and the server is expected to speak first.
43+ //
44+ // Similarly, we can't use ReadString or ReadBytes as these will keep reading
45+ // until the delimiter is found; an abusive client could easily disrupt a
46+ // server by sending a large amount of data that do not contain a LF byte.
47+ // Another means of attack would be to start connections and simply not send
48+ // data after the initial PROXY signature bytes, accumulating a large
49+ // number of blocked goroutines on the server. ReadSlice will also block for
50+ // a delimiter when the internal buffer does not fill up.
51+ //
52+ // A plain Read is also problematic since we risk reading past the end of the
53+ // header without being able to easily put the excess bytes back into the reader's
54+ // buffer (with the current implementation's design).
55+ //
56+ // So we use a ReadByte loop, which solves the overflow problem and avoids
57+ // reading beyond the end of the header. However, we need one more trick to harden
58+ // against partial header attacks (slow loris) - per spec:
59+ //
60+ // (..) The sender must always ensure that the header is sent at once, so that
61+ // the transport layer maintains atomicity along the path to the receiver. The
62+ // receiver may be tolerant to partial headers or may simply drop the connection
63+ // when receiving a partial header. Recommendation is to be tolerant, but
64+ // implementation constraints may not always easily permit this.
65+ //
66+ // We are subject to such implementation constraints. So we return an error if
67+ // the header cannot be fully extracted with a single read of the underlying
68+ // reader.
69+ buf := make ([]byte , 0 , 107 )
70+ for {
71+ b , err := reader .ReadByte ()
72+ if err != nil {
73+ return nil , fmt .Errorf (ErrCantReadVersion1Header .Error ()+ ": %v" , err )
74+ }
75+ buf = append (buf , b )
76+ if b == '\n' {
77+ // End of header found
78+ break
79+ }
80+ if len (buf ) == 107 {
81+ // No delimiter in first 107 bytes
82+ return nil , ErrVersion1HeaderTooLong
83+ }
84+ if reader .Buffered () == 0 {
85+ // Header was not buffered in a single read. Since we can't
86+ // differentiate between genuine slow writers and DoS agents,
87+ // we abort. On healthy networks, this should never happen.
88+ return nil , ErrCantReadVersion1Header
89+ }
90+ }
91+
92+ // Check for CR before LF.
93+ if len (buf ) < 2 || buf [len (buf )- 2 ] != '\r' {
3294 return nil , ErrLineMustEndWithCrlf
3395 }
96+
3497 // Check full signature.
35- tokens := strings .Split (line [:len (line )- 2 ], separator )
98+ tokens := strings .Split (string ( buf [:len (buf )- 2 ]) , separator )
3699
37100 // Expect at least 2 tokens: "PROXY" and the transport protocol.
38101 if len (tokens ) < 2 {
0 commit comments