github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/cmd/dockerd/hack/malformed_host_override.go (about) 1 // +build !windows 2 3 package hack 4 5 import "net" 6 7 // MalformedHostHeaderOverride is a wrapper to be able 8 // to overcome the 400 Bad request coming from old docker 9 // clients that send an invalid Host header. 10 type MalformedHostHeaderOverride struct { 11 net.Listener 12 } 13 14 // MalformedHostHeaderOverrideConn wraps the underlying unix 15 // connection and keeps track of the first read from http.Server 16 // which just reads the headers. 17 type MalformedHostHeaderOverrideConn struct { 18 net.Conn 19 first bool 20 } 21 22 var closeConnHeader = []byte("\r\nConnection: close\r") 23 24 // Read reads the first *read* request from http.Server to inspect 25 // the Host header. If the Host starts with / then we're talking to 26 // an old docker client which send an invalid Host header. To not 27 // error out in http.Server we rewrite the first bytes of the request 28 // to sanitize the Host header itself. 29 // In case we're not dealing with old docker clients the data is just passed 30 // to the server w/o modification. 31 func (l *MalformedHostHeaderOverrideConn) Read(b []byte) (n int, err error) { 32 // http.Server uses a 4k buffer 33 if l.first && len(b) == 4096 { 34 // This keeps track of the first read from http.Server which just reads 35 // the headers 36 l.first = false 37 // The first read of the connection by http.Server is done limited to 38 // DefaultMaxHeaderBytes (usually 1 << 20) + 4096. 39 // Here we do the first read which gets us all the http headers to 40 // be inspected and modified below. 41 c, err := l.Conn.Read(b) 42 if err != nil { 43 return c, err 44 } 45 46 var ( 47 start, end int 48 firstLineFeed = -1 49 buf []byte 50 ) 51 for i := 0; i <= c-1-7; i++ { 52 if b[i] == '\n' && firstLineFeed == -1 { 53 firstLineFeed = i 54 } 55 if b[i] != '\n' { 56 continue 57 } 58 59 if b[i+1] == '\r' && b[i+2] == '\n' { 60 return c, nil 61 } 62 63 if b[i+1] != 'H' { 64 continue 65 } 66 if b[i+2] != 'o' { 67 continue 68 } 69 if b[i+3] != 's' { 70 continue 71 } 72 if b[i+4] != 't' { 73 continue 74 } 75 if b[i+5] != ':' { 76 continue 77 } 78 if b[i+6] != ' ' { 79 continue 80 } 81 if b[i+7] != '/' { 82 continue 83 } 84 // ensure clients other than the docker clients do not get this hack 85 if i != firstLineFeed { 86 return c, nil 87 } 88 start = i + 7 89 // now find where the value ends 90 for ii, bbb := range b[start:c] { 91 if bbb == '\n' { 92 end = start + ii 93 break 94 } 95 } 96 buf = make([]byte, 0, c+len(closeConnHeader)-(end-start)) 97 // strip the value of the host header and 98 // inject `Connection: close` to ensure we don't reuse this connection 99 buf = append(buf, b[:start]...) 100 buf = append(buf, closeConnHeader...) 101 buf = append(buf, b[end:c]...) 102 copy(b, buf) 103 break 104 } 105 if len(buf) == 0 { 106 return c, nil 107 } 108 return len(buf), nil 109 } 110 return l.Conn.Read(b) 111 } 112 113 // Accept makes the listener accepts connections and wraps the connection 114 // in a MalformedHostHeaderOverrideConn initializing first to true. 115 func (l *MalformedHostHeaderOverride) Accept() (net.Conn, error) { 116 c, err := l.Listener.Accept() 117 if err != nil { 118 return c, err 119 } 120 return &MalformedHostHeaderOverrideConn{c, true}, nil 121 }