Skip to content

Commit dbc1514

Browse files
nathanejohnsonbradfitz
authored andcommitted
Adding the HostName field to the Conn struct (inetaf#18)
Changing the internal-only match interface to return any parsed hostnames. It can be useful for implementers of Target to be able to inspect the already-parsed SNI header (in the case of TLS) or host header (in the case of http) to know what host was asked for by the client in order to make additional routing decisions. This can be used by transparent reverse proxies where the destination is not known in advance.
1 parent 2b928d9 commit dbc1514

File tree

4 files changed

+36
-18
lines changed

4 files changed

+36
-18
lines changed

http.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ type httpHostMatch struct {
4646
target Target
4747
}
4848

49-
func (m httpHostMatch) match(br *bufio.Reader) Target {
50-
if m.matcher(context.TODO(), httpHostHeader(br)) {
51-
return m.target
49+
func (m httpHostMatch) match(br *bufio.Reader) (Target, string) {
50+
hh := httpHostHeader(br)
51+
if m.matcher(context.TODO(), hh) {
52+
return m.target, hh
5253
}
53-
return nil
54+
return nil, ""
5455
}
5556

5657
// httpHostHeader returns the HTTP Host header from br without

sni.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,12 @@ type sniMatch struct {
7373
target Target
7474
}
7575

76-
func (m sniMatch) match(br *bufio.Reader) Target {
77-
if m.matcher(context.TODO(), clientHelloServerName(br)) {
78-
return m.target
76+
func (m sniMatch) match(br *bufio.Reader) (Target, string) {
77+
sni := clientHelloServerName(br)
78+
if m.matcher(context.TODO(), sni) {
79+
return m.target, sni
7980
}
80-
return nil
81+
return nil, ""
8182
}
8283

8384
// acmeMatch matches "*.acme.invalid" ACME tls-sni-01 challenges and
@@ -87,10 +88,10 @@ type acmeMatch struct {
8788
cfg *config
8889
}
8990

90-
func (m *acmeMatch) match(br *bufio.Reader) Target {
91+
func (m *acmeMatch) match(br *bufio.Reader) (Target, string) {
9192
sni := clientHelloServerName(br)
9293
if !strings.HasSuffix(sni, ".acme.invalid") {
93-
return nil
94+
return nil, ""
9495
}
9596

9697
// TODO: cache. ACME issuers will hit multiple times in a short
@@ -107,12 +108,12 @@ func (m *acmeMatch) match(br *bufio.Reader) Target {
107108
}
108109
for range m.cfg.acmeTargets {
109110
if target := <-ch; target != nil {
110-
return target
111+
return target, sni
111112
}
112113
}
113114

114115
// No target was happy with the provided challenge.
115-
return nil
116+
return nil, ""
116117
}
117118

118119
func tryACME(ctx context.Context, ch chan<- Target, dest Target, sni string) {

tcpproxy.go

+17-5
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,10 @@ type route interface {
107107
//
108108
// match must not consume bytes from the given bufio.Reader, it
109109
// can only Peek.
110-
match(*bufio.Reader) Target
110+
//
111+
// If an sni or host header was parsed successfully, that will be
112+
// returned as the second parameter.
113+
match(*bufio.Reader) (Target, string)
111114
}
112115

113116
func (p *Proxy) netListen() func(net, laddr string) (net.Listener, error) {
@@ -147,7 +150,7 @@ type fixedTarget struct {
147150
t Target
148151
}
149152

150-
func (m fixedTarget) match(*bufio.Reader) Target { return m.t }
153+
func (m fixedTarget) match(*bufio.Reader) (Target, string) { return m.t, "" }
151154

152155
// Run is calls Start, and then Wait.
153156
//
@@ -224,12 +227,13 @@ func (p *Proxy) serveListener(ret chan<- error, ln net.Listener, routes []route)
224227
func (p *Proxy) serveConn(c net.Conn, routes []route) bool {
225228
br := bufio.NewReader(c)
226229
for _, route := range routes {
227-
if target := route.match(br); target != nil {
230+
if target, hostName := route.match(br); target != nil {
228231
if n := br.Buffered(); n > 0 {
229232
peeked, _ := br.Peek(br.Buffered())
230233
c = &Conn{
231-
Peeked: peeked,
232-
Conn: c,
234+
HostName: hostName,
235+
Peeked: peeked,
236+
Conn: c,
233237
}
234238
}
235239
target.HandleConn(c)
@@ -246,6 +250,14 @@ func (p *Proxy) serveConn(c net.Conn, routes []route) bool {
246250
// to determine how to route the connection. The Read method stitches
247251
// the peeked bytes and unread bytes back together.
248252
type Conn struct {
253+
// HostName is the hostname field that was sent to the request router.
254+
// In the case of TLS, this is the SNI header, in the case of HTTPHost
255+
// route, it will be the host header. In the case of a fixed
256+
// route, i.e. those created with AddRoute(), this will always be
257+
// empty. This can be useful in the case where further routing decisions
258+
// need to be made in the Target impementation.
259+
HostName string
260+
249261
// Peeked are the bytes that have been read from Conn for the
250262
// purposes of route matching, but have not yet been consumed
251263
// by Read calls. It set to nil by Read when fully consumed.

tcpproxy_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,14 @@ func TestMatchHTTPHost(t *testing.T) {
7272
t.Run(name, func(t *testing.T) {
7373
br := bufio.NewReader(tt.r)
7474
r := httpHostMatch{equals(tt.host), noopTarget{}}
75-
got := r.match(br) != nil
75+
m, name := r.match(br)
76+
got := m != nil
7677
if got != tt.want {
7778
t.Fatalf("match = %v; want %v", got, tt.want)
7879
}
80+
if tt.want && name != tt.host {
81+
t.Fatalf("host = %s; want %s", name, tt.host)
82+
}
7983
get := make([]byte, 3)
8084
if _, err := io.ReadFull(br, get); err != nil {
8185
t.Fatal(err)

0 commit comments

Comments
 (0)