-
Notifications
You must be signed in to change notification settings - Fork 0
/
sni.go
105 lines (91 loc) · 3.39 KB
/
sni.go
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
// NOTE: A lot of this code was copied from this very helpful article:
// https://www.agwa.name/blog/post/writing_an_sni_proxy_in_go
package waygate
import (
"bytes"
"crypto/tls"
"io"
"net"
"time"
)
type readOnlyConn struct {
reader io.Reader
}
func (conn readOnlyConn) Read(p []byte) (int, error) { return conn.reader.Read(p) }
func (conn readOnlyConn) Write(p []byte) (int, error) { return 0, io.ErrClosedPipe }
func (conn readOnlyConn) Close() error { return nil }
func (conn readOnlyConn) LocalAddr() net.Addr { return nil }
func (conn readOnlyConn) RemoteAddr() net.Addr { return nil }
func (conn readOnlyConn) SetDeadline(t time.Time) error { return nil }
func (conn readOnlyConn) SetReadDeadline(t time.Time) error { return nil }
func (conn readOnlyConn) SetWriteDeadline(t time.Time) error { return nil }
func peekClientHello(reader io.Reader) (*tls.ClientHelloInfo, io.Reader, error) {
peekedBytes := new(bytes.Buffer)
hello, err := readClientHello(io.TeeReader(reader, peekedBytes))
if err != nil {
return nil, nil, err
}
return hello, io.MultiReader(peekedBytes, reader), nil
}
func readClientHello(reader io.Reader) (*tls.ClientHelloInfo, error) {
var hello *tls.ClientHelloInfo
err := tls.Server(readOnlyConn{reader: reader}, &tls.Config{
GetConfigForClient: func(argHello *tls.ClientHelloInfo) (*tls.Config, error) {
hello = new(tls.ClientHelloInfo)
*hello = *argHello
return nil, nil
},
}).Handshake()
if hello == nil {
return nil, err
}
return hello, nil
}
type PassthroughListener struct {
ch chan net.Conn
}
func NewPassthroughListener() *PassthroughListener {
return &PassthroughListener{
ch: make(chan net.Conn),
}
}
func (f *PassthroughListener) Accept() (net.Conn, error) {
conn := <-f.ch
return conn, nil
}
func (f *PassthroughListener) Close() error {
return nil
}
func (f *PassthroughListener) Addr() net.Addr {
return nil
}
func (f *PassthroughListener) PassConn(conn net.Conn) {
f.ch <- conn
}
// This type creates a new net.Conn that's the same as an old one, except a new
// reader is provided. So it proxies every method except Read. This is
// necessary because by calling peekClientHello, part of the reader is read,
// so we need to create a new reader with the already read data inserted back
// in the front.
// I'm sure there's a cleaner way to do this...
type ProxyConn struct {
conn net.Conn
reader io.Reader
}
func NewProxyConn(conn net.Conn, reader io.Reader) *ProxyConn {
return &ProxyConn{
conn,
reader,
}
}
func (c ProxyConn) CloseWrite() error { return c.conn.(*net.TCPConn).CloseWrite() }
func (c ProxyConn) Read(p []byte) (int, error) { return c.reader.Read(p) }
func (c ProxyConn) Write(p []byte) (int, error) { return c.conn.Write(p) }
// TODO: is this safe? Will it actually close properly, or does it need to be
// connected to the reader somehow?
func (c ProxyConn) Close() error { return c.conn.Close() }
func (c ProxyConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
func (c ProxyConn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() }
func (c ProxyConn) SetDeadline(t time.Time) error { return c.conn.SetDeadline(t) }
func (c ProxyConn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) }
func (c ProxyConn) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) }