github.com/go/docker@v1.12.0-rc2/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 initilizing 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  }