File: malformed_host_override.go

package info (click to toggle)
docker.io 1.11.2~ds1-6
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 23,652 kB
  • ctags: 12,672
  • sloc: sh: 7,238; makefile: 340; ansic: 123; xml: 62
file content (121 lines) | stat: -rw-r--r-- 3,114 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// +build !windows

package 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 initilizing 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
}