| // +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 |
| } |