1package traffic
2
3import (
4 "bytes"
5 "crypto/tls"
6 "fmt"
7 "maps"
8 "net"
9 "net/http"
10 "strings"
11 "sync"
12 "time"
13
14 "github.com/bnema/gordon/internal/domain"
15)
16
17const (
18 maxClientHelloBytes = 64 << 10
19 clientHelloReadTimeout = 250 * time.Millisecond
20)
21
22// TLSHTTPServerConfig describes the HTTPS server attached to a tls_mux entrypoint.
23type TLSHTTPServerConfig struct {
24 Handler http.Handler
25 TLSConfig *tls.Config
26}
27
28type tlsHTTPServers map[string]TLSHTTPServerConfig
29
30// SetTLSHTTPServer installs or removes the HTTPS server for a tls_mux entrypoint.
31func (m *Manager) SetTLSHTTPServer(entryPoint string, handler http.Handler, tlsConfig *tls.Config) {
32 m.mu.Lock()
33 current := m.loadTLSHTTPServers()
34 next := make(tlsHTTPServers, len(current)+1)
35 maps.Copy(next, current)
36 if handler == nil || tlsConfig == nil {
37 delete(next, entryPoint)
38 } else {
39 next[entryPoint] = TLSHTTPServerConfig{Handler: handler, TLSConfig: tlsConfig.Clone()}
40 }
41 m.tlsHTTPServers.Store(next)
42 runtimes := make([]*entryPointRuntime, 0, len(m.listeners))
43 for _, runtime := range m.listeners {
44 if runtime.entryPointSnapshot().Name == entryPoint {
45 runtimes = append(runtimes, runtime)
46 }
47 }
48 m.mu.Unlock()
49
50 for _, runtime := range runtimes {
51 runtime.refreshTLSHTTPServer(entryPoint)
52 }
53}
54
55func (m *Manager) tlsHTTPServer(entryPoint string) (TLSHTTPServerConfig, bool) {
56 config, ok := m.loadTLSHTTPServers()[entryPoint]
57 return config, ok
58}
59
60func (m *Manager) loadTLSHTTPServers() tlsHTTPServers {
61 value := m.tlsHTTPServers.Load()
62 if value == nil {
63 return tlsHTTPServers{}
64 }
65 return value.(tlsHTTPServers)
66}
67
68func peekClientHelloSNI(conn net.Conn) (string, net.Conn, error) {
69 return peekClientHelloSNIWithLimit(conn, maxClientHelloBytes)
70}
71
72func peekClientHelloSNIWithLimit(conn net.Conn, maxBytes int) (string, net.Conn, error) {
73 if err := conn.SetReadDeadline(time.Now().Add(clientHelloReadTimeout)); err != nil {
74 return "", nil, fmt.Errorf("set client hello read deadline: %w", err)
75 }
76 defer func() { _ = conn.SetReadDeadline(time.Time{}) }()
77
78 buf := make([]byte, 0, 4096)
79 for len(buf) < maxBytes {
80 remaining := maxBytes - len(buf)
81 readSize := min(1024, remaining)
82 tmp := make([]byte, readSize)
83 n, err := conn.Read(tmp)
84 if n > 0 {
85 buf = append(buf, tmp[:n]...)
86 sni, complete, parseErr := parseClientHelloSNI(buf)
87 if parseErr != nil {
88 return "", nil, fmt.Errorf("parse client hello sni: %w", parseErr)
89 }
90 if complete {
91 return sni, replayConn{Conn: conn, reader: bytes.NewReader(buf)}, nil
92 }
93 }
94 if err != nil {
95 return "", nil, fmt.Errorf("read client hello: %w", err)
96 }
97 }
98 return "", nil, fmt.Errorf("%w: %d bytes", domain.ErrClientHelloTooLarge, maxBytes)
99}
100
101func parseClientHelloSNI(data []byte) (string, bool, error) {
102 if len(data) < 5 {
103 return "", false, nil
104 }
105 if data[0] != 22 {
106 return "", false, fmt.Errorf("tls record is not a handshake")
107 }
108 record, consumed, complete, err := tlsRecordPayload(data, 0)
109 if err != nil || !complete {
110 return "", complete, err
111 }
112 handshake := append([]byte(nil), record...)
113 for len(handshake) < 4 {
114 if len(data) < consumed+5 {
115 return "", false, nil
116 }
117 next, nextConsumed, nextComplete, err := tlsRecordPayload(data, consumed)
118 if err != nil || !nextComplete {
119 return "", nextComplete, err
120 }
121 handshake = append(handshake, next...)
122 consumed = nextConsumed
123 }
124 if handshake[0] != 1 {
125 return "", true, fmt.Errorf("tls handshake is not client hello")
126 }
127 handshakeLen := int(handshake[1])<<16 | int(handshake[2])<<8 | int(handshake[3])
128 for len(handshake)-4 < handshakeLen {
129 if len(data) < consumed+5 {
130 return "", false, nil
131 }
132 next, nextConsumed, nextComplete, err := tlsRecordPayload(data, consumed)
133 if err != nil || !nextComplete {
134 return "", nextComplete, err
135 }
136 handshake = append(handshake, next...)
137 consumed = nextConsumed
138 }
139 sni, err := parseClientHelloExtensions(handshake[4 : 4+handshakeLen])
140 return sni, true, err
141}
142
143func tlsRecordPayload(data []byte, offset int) ([]byte, int, bool, error) {
144 if data[offset] != 22 {
145 return nil, offset, false, fmt.Errorf("tls record is not a handshake")
146 }
147 recordLen := int(data[offset+3])<<8 | int(data[offset+4])
148 if recordLen <= 0 {
149 return nil, offset, false, fmt.Errorf("tls handshake record is empty")
150 }
151 end := offset + 5 + recordLen
152 if len(data) < end {
153 return nil, offset, false, nil
154 }
155 return data[offset+5 : end], end, true, nil
156}
157
158func parseClientHelloExtensions(body []byte) (string, error) {
159 reader := bytes.NewReader(body)
160 if reader.Len() < 34 {
161 return "", fmt.Errorf("client hello is truncated")
162 }
163 _, _ = readBytes(reader, 34) // legacy_version + random
164 if err := skipLengthPrefixed(reader, 1); err != nil {
165 return "", err
166 }
167 if err := skipLengthPrefixed(reader, 2); err != nil {
168 return "", err
169 }
170 if err := skipLengthPrefixed(reader, 1); err != nil {
171 return "", err
172 }
173 if reader.Len() == 0 {
174 return "", nil
175 }
176 extLen, err := readUint16(reader)
177 if err != nil {
178 return "", err
179 }
180 if extLen > reader.Len() {
181 return "", fmt.Errorf("client hello extensions are truncated")
182 }
183 extBytes, _ := readBytes(reader, extLen)
184 extensions := bytes.NewReader(extBytes)
185 for extensions.Len() > 0 {
186 extType, err := readUint16(extensions)
187 if err != nil {
188 return "", err
189 }
190 extDataLen, err := readUint16(extensions)
191 if err != nil {
192 return "", err
193 }
194 if extDataLen > extensions.Len() {
195 return "", fmt.Errorf("client hello extension is truncated")
196 }
197 extData, _ := readBytes(extensions, extDataLen)
198 if extType == 0 {
199 return parseSNIExtension(extData)
200 }
201 }
202 return "", nil
203}
204
205func parseSNIExtension(data []byte) (string, error) {
206 reader := bytes.NewReader(data)
207 listLen, err := readUint16(reader)
208 if err != nil {
209 return "", err
210 }
211 if listLen > reader.Len() {
212 return "", fmt.Errorf("sni extension is truncated")
213 }
214 listBytes, _ := readBytes(reader, listLen)
215 list := bytes.NewReader(listBytes)
216 for list.Len() > 0 {
217 nameType, err := list.ReadByte()
218 if err != nil {
219 return "", err
220 }
221 nameLen, err := readUint16(list)
222 if err != nil {
223 return "", err
224 }
225 if nameLen > list.Len() {
226 return "", fmt.Errorf("sni name is truncated")
227 }
228 nameBytes, _ := readBytes(list, nameLen)
229 name := string(nameBytes)
230 if nameType == 0 {
231 return normalizeTLSName(name), nil
232 }
233 }
234 return "", nil
235}
236
237func skipLengthPrefixed(reader *bytes.Reader, size int) error {
238 length, err := readLength(reader, size)
239 if err != nil {
240 return err
241 }
242 if length > reader.Len() {
243 return fmt.Errorf("client hello field is truncated")
244 }
245 _, _ = readBytes(reader, length)
246 return nil
247}
248
249func readBytes(reader *bytes.Reader, size int) ([]byte, error) {
250 if reader.Len() < size {
251 return nil, fmt.Errorf("client hello field is truncated")
252 }
253 value := make([]byte, size)
254 _, _ = reader.Read(value)
255 return value, nil
256}
257
258func readLength(reader *bytes.Reader, size int) (int, error) {
259 if reader.Len() < size {
260 return 0, fmt.Errorf("client hello length is truncated")
261 }
262 value := 0
263 for range size {
264 b, _ := reader.ReadByte()
265 value = value<<8 | int(b)
266 }
267 return value, nil
268}
269
270func readUint16(reader *bytes.Reader) (int, error) {
271 return readLength(reader, 2)
272}
273
274func normalizeTLSName(value string) string {
275 return strings.TrimSuffix(strings.ToLower(strings.TrimSpace(value)), ".")
276}
277
278type replayConn struct {
279 net.Conn
280 reader *bytes.Reader
281}
282
283func (c replayConn) Read(p []byte) (int, error) {
284 if c.reader != nil && c.reader.Len() > 0 {
285 return c.reader.Read(p)
286 }
287 return c.Conn.Read(p)
288}
289
290type tlsHTTPListener struct {
291 addr net.Addr
292 conns chan net.Conn
293 done chan struct{}
294 once sync.Once
295 mu sync.Mutex
296 closed bool
297}
298
299func newTLSHTTPListener(addr net.Addr) *tlsHTTPListener {
300 return &tlsHTTPListener{addr: addr, conns: make(chan net.Conn, 128), done: make(chan struct{})}
301}
302
303func (l *tlsHTTPListener) Accept() (net.Conn, error) {
304 select {
305 case <-l.done:
306 return nil, net.ErrClosed
307 default:
308 }
309 select {
310 case conn := <-l.conns:
311 if l.isClosed() {
312 _ = conn.Close()
313 return nil, net.ErrClosed
314 }
315 return conn, nil
316 case <-l.done:
317 return nil, net.ErrClosed
318 }
319}
320
321func (l *tlsHTTPListener) Close() error {
322 l.once.Do(func() {
323 l.mu.Lock()
324 l.closed = true
325 l.mu.Unlock()
326 close(l.done)
327 for {
328 select {
329 case conn := <-l.conns:
330 _ = conn.Close()
331 default:
332 return
333 }
334 }
335 })
336 return nil
337}
338
339func (l *tlsHTTPListener) Addr() net.Addr { return l.addr }
340
341func (l *tlsHTTPListener) serve(conn net.Conn) bool {
342 l.mu.Lock()
343 defer l.mu.Unlock()
344 if l.closed {
345 return false
346 }
347 select {
348 case l.conns <- conn:
349 return true
350 default:
351 return false
352 }
353}
354
355func (l *tlsHTTPListener) isClosed() bool {
356 l.mu.Lock()
357 defer l.mu.Unlock()
358 return l.closed
359}