// +build !windows package hack // import "github.com/docker/docker/cmd/dockerd/hack" import "net" // MalformedHostHeaderOverride is a wrapper to be able // to overcome the 400 Bad request coming from old docker // clients that send an invalid Host header. type MalformedHostHeaderOverride struct { net.Listener } // MalformedHostHeaderOverrideConn wraps the underlying unix // connection and keeps track of the first read from http.Server // which just reads the headers. type MalformedHostHeaderOverrideConn struct { net.Conn first bool } var closeConnHeader = []byte("\r\nConnection: close\r") // Read reads the first *read* request from http.Server to inspect // the Host header. If the Host starts with / then we're talking to // an old docker client which send an invalid Host header. To not // error out in http.Server we rewrite the first bytes of the request // to sanitize the Host header itself. // In case we're not dealing with old docker clients the data is just passed // to the server w/o modification. func (l *MalformedHostHeaderOverrideConn) Read(b []byte) (n int, err error) { // http.Server uses a 4k buffer if l.first && len(b) == 4096 { // This keeps track of the first read from http.Server which just reads // the headers l.first = false // The first read of the connection by http.Server is done limited to // DefaultMaxHeaderBytes (usually 1 << 20) + 4096. // Here we do the first read which gets us all the http headers to // be inspected and modified below. c, err := l.Conn.Read(b) if err != nil { return c, err } var ( start, end int firstLineFeed = -1 buf []byte ) for i := 0; i <= c-1-7; i++ { if b[i] == '\n' && firstLineFeed == -1 { firstLineFeed = i } if b[i] != '\n' { continue } if b[i+1] == '\r' && b[i+2] == '\n' { return c, nil } if b[i+1] != 'H' { continue } if b[i+2] != 'o' { continue } if b[i+3] != 's' { continue } if b[i+4] != 't' { continue } if b[i+5] != ':' { continue } if b[i+6] != ' ' { continue } if b[i+7] != '/' { continue } // ensure clients other than the docker clients do not get this hack if i != firstLineFeed { return c, nil } start = i + 7 // now find where the value ends for ii, bbb := range b[start:c] { if bbb == '\n' { end = start + ii break } } buf = make([]byte, 0, c+len(closeConnHeader)-(end-start)) // strip the value of the host header and // inject `Connection: close` to ensure we don't reuse this connection buf = append(buf, b[:start]...) buf = append(buf, closeConnHeader...) buf = append(buf, b[end:c]...) copy(b, buf) break } if len(buf) == 0 { return c, nil } return len(buf), nil } return l.Conn.Read(b) } // Accept makes the listener accepts connections and wraps the connection // in a MalformedHostHeaderOverrideConn initializing first to true. func (l *MalformedHostHeaderOverride) Accept() (net.Conn, error) { c, err := l.Listener.Accept() if err != nil { return c, err } return &MalformedHostHeaderOverrideConn{c, true}, nil }