Files
x/handler/tunnel/entrypoint/eptls.go
T
ginuerzh 81db46b725 refactor(handler/tunnel): code review fixes and entrypoint subpackage extraction
- Fix dead-code branch in bind.go host assignment (always use endpoint hash)
- Return descriptive error on bypass match in connect.go (was masking as success)
- Update bypass test in connect_test.go for new error behavior
- Extract entrypoint subpackage from monolithic entrypoint.go (6 files)
- Fix observeStats event-loss bug (break -> fallthrough on retry success)
- Add 47 unit tests across handler, connect, bind, metadata packages
- Add architecture doc comments to all key files
- Build and vet clean, 173 tests pass with -race
2026-06-02 23:51:55 +08:00

94 lines
2.6 KiB
Go

package entrypoint
import (
"bytes"
"context"
"encoding/hex"
"io"
"net"
"strings"
"time"
"github.com/go-gost/core/logger"
dissector "github.com/go-gost/tls-dissector"
ictx "github.com/go-gost/x/internal/ctx"
xio "github.com/go-gost/x/internal/io"
xnet "github.com/go-gost/x/internal/net"
tls_util "github.com/go-gost/x/internal/util/tls"
xrecorder "github.com/go-gost/x/recorder"
)
// handleTLS processes a TLS connection arriving at the entrypoint.
//
// Flow:
// 1. Parse ClientHello (tee-reads first bytes for recording).
// 2. Extract SNI hostname → ingress lookup → tunnelID.
// 3. ep.dial() → Dialer.Dial() → mux stream (or SD TCP connection).
// 4. Write buffered ClientHello bytes to the mux stream.
// 5. Parse ServerHello from the mux stream (for TLS recording).
// 6. Write ServerHello bytes back to the public connection.
// 7. Pipe(publicConn, muxStream) — bidirectional TLS passthrough.
func (ep *Entrypoint) handleTLS(ctx context.Context, conn net.Conn, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
buf := new(bytes.Buffer)
clientHello, err := dissector.ParseClientHello(io.TeeReader(conn, buf))
if err != nil {
return err
}
ro.TLS = &xrecorder.TLSRecorderObject{
ServerName: clientHello.ServerName,
ClientHello: hex.EncodeToString(buf.Bytes()),
}
if len(clientHello.SupportedProtos) > 0 {
ro.TLS.Proto = clientHello.SupportedProtos[0]
}
host := clientHello.ServerName
if host != "" {
if _, _, err := net.SplitHostPort(host); err != nil {
host = net.JoinHostPort(strings.Trim(host, "[]"), "443")
}
ro.Host = host
}
// ctx = xctx.ContextWithClientAddr(ctx, xctx.ClientAddr(ro.RemoteAddr))
ctx = ictx.ContextWithRecorderObject(ctx, ro)
ctx = ictx.ContextWithLogger(ctx, log)
cc, err := ep.dial(ctx, "tcp", host)
if err != nil {
return err
}
defer cc.Close()
if _, err := buf.WriteTo(cc); err != nil {
return err
}
xio.SetReadDeadline(cc, time.Now().Add(ep.readTimeout))
serverHello, err := dissector.ParseServerHello(io.TeeReader(cc, buf))
xio.SetReadDeadline(cc, time.Time{})
if serverHello != nil {
ro.TLS.CipherSuite = tls_util.CipherSuite(serverHello.CipherSuite).String()
ro.TLS.CompressionMethod = serverHello.CompressionMethod
if serverHello.Proto != "" {
ro.TLS.Proto = serverHello.Proto
}
if serverHello.Version > 0 {
ro.TLS.Version = tls_util.Version(serverHello.Version).String()
}
}
if buf.Len() > 0 {
ro.TLS.ServerHello = hex.EncodeToString(buf.Bytes())
}
if _, err := buf.WriteTo(conn); err != nil {
return err
}
// xnet.Transport(conn, cc)
xnet.Pipe(ctx, conn, cc)
return err
}