Files
x/dialer/utls/dialer.go
T
2026-06-28 12:08:30 +08:00

107 lines
2.5 KiB
Go

package utls
import (
"context"
"crypto/tls"
"net"
"time"
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
xnet "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/registry"
"github.com/go-gost/x/util"
utls "github.com/refraction-networking/utls"
)
func init() {
registry.DialerRegistry().Register("utls", NewDialer)
}
type utlsDialer struct {
md metadata
log logger.Logger
options dialer.Options
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
options := dialer.Options{}
for _, opt := range opts {
opt(&options)
}
return &utlsDialer{
log: options.Logger,
options: options,
}
}
func (d *utlsDialer) Init(md md.Metadata) (err error) {
return d.parseMetadata(md)
}
func (d *utlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
conn, err := options.Dialer.Dial(ctx, "tcp", addr)
if err != nil {
d.log.Error(err)
}
if d.md.keepalive {
xnet.ApplyKeepalive(conn, net.KeepAliveConfig{
Enable: true,
Idle: d.md.keepaliveIdle,
Interval: d.md.keepaliveInterval,
Count: d.md.keepaliveCount,
})
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
return conn, err
}
// Handshake implements dialer.Handshaker.
func (d *utlsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dialer.HandshakeOption) (net.Conn, error) {
if d.md.handshakeTimeout > 0 {
conn.SetDeadline(time.Now().Add(d.md.handshakeTimeout))
defer conn.SetDeadline(time.Time{})
}
clientHelloID, ok := GetClientHelloID(d.md.fingerprint)
if ok {
d.log.Debugf("utls handshake with fingerprint: %s", d.md.fingerprint)
utlsCfg := util.NewUTLSConfig(d.options.TLSConfig)
uconn := utls.UClient(conn, utlsCfg, clientHelloID)
if err := uconn.HandshakeContext(ctx); err != nil {
conn.Close()
return nil, err
}
return uconn, nil
}
// fingerprint not set, empty, "golang", or unknown: fall through to
// standard crypto/tls.
if d.md.fingerprint != "" {
d.log.Warnf("unknown utls fingerprint: %s, falling back to standard TLS", d.md.fingerprint)
}
tlsConn := tls.Client(conn, d.options.TLSConfig)
if err := tlsConn.HandshakeContext(ctx); err != nil {
conn.Close()
return nil, err
}
return tlsConn, nil
}