github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/transport/internet/tls/tls.go (about)

     1  package tls
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"crypto/tls"
     7  	"math/big"
     8  	"time"
     9  
    10  	utls "github.com/refraction-networking/utls"
    11  	"github.com/xmplusdev/xmcore/common/buf"
    12  	"github.com/xmplusdev/xmcore/common/net"
    13  )
    14  
    15  //go:generate go run github.com/xmplusdev/xmcore/common/errors/errorgen
    16  
    17  type Interface interface {
    18  	net.Conn
    19  	HandshakeContext(ctx context.Context) error
    20  	VerifyHostname(host string) error
    21  	NegotiatedProtocol() (name string, mutual bool)
    22  }
    23  
    24  var _ buf.Writer = (*Conn)(nil)
    25  
    26  type Conn struct {
    27  	*tls.Conn
    28  }
    29  
    30  const tlsCloseTimeout = 250 * time.Millisecond
    31  
    32  func (c *Conn) Close() error {
    33  	timer := time.AfterFunc(tlsCloseTimeout, func() {
    34  		c.Conn.NetConn().Close()
    35  	})
    36  	defer timer.Stop()
    37  	return c.Conn.Close()
    38  }
    39  
    40  func (c *Conn) WriteMultiBuffer(mb buf.MultiBuffer) error {
    41  	mb = buf.Compact(mb)
    42  	mb, err := buf.WriteMultiBuffer(c, mb)
    43  	buf.ReleaseMulti(mb)
    44  	return err
    45  }
    46  
    47  func (c *Conn) HandshakeAddressContext(ctx context.Context) net.Address {
    48  	if err := c.HandshakeContext(ctx); err != nil {
    49  		return nil
    50  	}
    51  	state := c.ConnectionState()
    52  	if state.ServerName == "" {
    53  		return nil
    54  	}
    55  	return net.ParseAddress(state.ServerName)
    56  }
    57  
    58  func (c *Conn) NegotiatedProtocol() (name string, mutual bool) {
    59  	state := c.ConnectionState()
    60  	return state.NegotiatedProtocol, state.NegotiatedProtocolIsMutual
    61  }
    62  
    63  // Client initiates a TLS client handshake on the given connection.
    64  func Client(c net.Conn, config *tls.Config) net.Conn {
    65  	tlsConn := tls.Client(c, config)
    66  	return &Conn{Conn: tlsConn}
    67  }
    68  
    69  // Server initiates a TLS server handshake on the given connection.
    70  func Server(c net.Conn, config *tls.Config) net.Conn {
    71  	tlsConn := tls.Server(c, config)
    72  	return &Conn{Conn: tlsConn}
    73  }
    74  
    75  type UConn struct {
    76  	*utls.UConn
    77  }
    78  
    79  func (c *UConn) Close() error {
    80  	timer := time.AfterFunc(tlsCloseTimeout, func() {
    81  		c.Conn.NetConn().Close()
    82  	})
    83  	defer timer.Stop()
    84  	return c.Conn.Close()
    85  }
    86  
    87  func (c *UConn) HandshakeAddressContext(ctx context.Context) net.Address {
    88  	if err := c.HandshakeContext(ctx); err != nil {
    89  		return nil
    90  	}
    91  	state := c.ConnectionState()
    92  	if state.ServerName == "" {
    93  		return nil
    94  	}
    95  	return net.ParseAddress(state.ServerName)
    96  }
    97  
    98  // WebsocketHandshake basically calls UConn.Handshake inside it but it will only send
    99  // http/1.1 in its ALPN.
   100  func (c *UConn) WebsocketHandshakeContext(ctx context.Context) error {
   101  	// Build the handshake state. This will apply every variable of the TLS of the
   102  	// fingerprint in the UConn
   103  	if err := c.BuildHandshakeState(); err != nil {
   104  		return err
   105  	}
   106  	// Iterate over extensions and check for utls.ALPNExtension
   107  	hasALPNExtension := false
   108  	for _, extension := range c.Extensions {
   109  		if alpn, ok := extension.(*utls.ALPNExtension); ok {
   110  			hasALPNExtension = true
   111  			alpn.AlpnProtocols = []string{"http/1.1"}
   112  			break
   113  		}
   114  	}
   115  	if !hasALPNExtension { // Append extension if doesn't exists
   116  		c.Extensions = append(c.Extensions, &utls.ALPNExtension{AlpnProtocols: []string{"http/1.1"}})
   117  	}
   118  	// Rebuild the client hello and do the handshake
   119  	if err := c.BuildHandshakeState(); err != nil {
   120  		return err
   121  	}
   122  	return c.HandshakeContext(ctx)
   123  }
   124  
   125  func (c *UConn) NegotiatedProtocol() (name string, mutual bool) {
   126  	state := c.ConnectionState()
   127  	return state.NegotiatedProtocol, state.NegotiatedProtocolIsMutual
   128  }
   129  
   130  func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) net.Conn {
   131  	utlsConn := utls.UClient(c, copyConfig(config), *fingerprint)
   132  	return &UConn{UConn: utlsConn}
   133  }
   134  
   135  func copyConfig(c *tls.Config) *utls.Config {
   136  	return &utls.Config{
   137  		RootCAs:               c.RootCAs,
   138  		ServerName:            c.ServerName,
   139  		InsecureSkipVerify:    c.InsecureSkipVerify,
   140  		VerifyPeerCertificate: c.VerifyPeerCertificate,
   141  		KeyLogWriter:          c.KeyLogWriter,
   142  	}
   143  }
   144  
   145  func init() {
   146  	bigInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(ModernFingerprints))))
   147  	stopAt := int(bigInt.Int64())
   148  	i := 0
   149  	for _, v := range ModernFingerprints {
   150  		if i == stopAt {
   151  			PresetFingerprints["random"] = v
   152  			break
   153  		}
   154  		i++
   155  	}
   156  	weights := utls.DefaultWeights
   157  	weights.TLSVersMax_Set_VersionTLS13 = 1
   158  	weights.FirstKeyShare_Set_CurveP256 = 0
   159  	randomized := utls.HelloRandomized
   160  	randomized.Seed, _ = utls.NewPRNGSeed()
   161  	randomized.Weights = &weights
   162  	PresetFingerprints["randomized"] = &randomized
   163  }
   164  
   165  func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) {
   166  	if name == "" {
   167  		return
   168  	}
   169  	if fingerprint = PresetFingerprints[name]; fingerprint != nil {
   170  		return
   171  	}
   172  	if fingerprint = ModernFingerprints[name]; fingerprint != nil {
   173  		return
   174  	}
   175  	if fingerprint = OtherFingerprints[name]; fingerprint != nil {
   176  		return
   177  	}
   178  	return
   179  }
   180  
   181  var PresetFingerprints = map[string]*utls.ClientHelloID{
   182  	// Recommended preset options in GUI clients
   183  	"chrome":     &utls.HelloChrome_Auto,
   184  	"firefox":    &utls.HelloFirefox_Auto,
   185  	"safari":     &utls.HelloSafari_Auto,
   186  	"ios":        &utls.HelloIOS_Auto,
   187  	"android":    &utls.HelloAndroid_11_OkHttp,
   188  	"edge":       &utls.HelloEdge_Auto,
   189  	"360":        &utls.Hello360_Auto,
   190  	"qq":         &utls.HelloQQ_Auto,
   191  	"random":     nil,
   192  	"randomized": nil,
   193  }
   194  
   195  var ModernFingerprints = map[string]*utls.ClientHelloID{
   196  	// One of these will be chosen as `random` at startup
   197  	"hellofirefox_99":         &utls.HelloFirefox_99,
   198  	"hellofirefox_102":        &utls.HelloFirefox_102,
   199  	"hellofirefox_105":        &utls.HelloFirefox_105,
   200  	"hellochrome_83":          &utls.HelloChrome_83,
   201  	"hellochrome_87":          &utls.HelloChrome_87,
   202  	"hellochrome_96":          &utls.HelloChrome_96,
   203  	"hellochrome_100":         &utls.HelloChrome_100,
   204  	"hellochrome_102":         &utls.HelloChrome_102,
   205  	"hellochrome_106_shuffle": &utls.HelloChrome_106_Shuffle,
   206  	"helloios_13":             &utls.HelloIOS_13,
   207  	"helloios_14":             &utls.HelloIOS_14,
   208  	"helloedge_85":            &utls.HelloEdge_85,
   209  	"helloedge_106":           &utls.HelloEdge_106,
   210  	"hellosafari_16_0":        &utls.HelloSafari_16_0,
   211  	"hello360_11_0":           &utls.Hello360_11_0,
   212  	"helloqq_11_1":            &utls.HelloQQ_11_1,
   213  }
   214  
   215  var OtherFingerprints = map[string]*utls.ClientHelloID{
   216  	// Golang, randomized, auto, and fingerprints that are too old
   217  	"hellogolang":            &utls.HelloGolang,
   218  	"hellorandomized":        &utls.HelloRandomized,
   219  	"hellorandomizedalpn":    &utls.HelloRandomizedALPN,
   220  	"hellorandomizednoalpn":  &utls.HelloRandomizedNoALPN,
   221  	"hellofirefox_auto":      &utls.HelloFirefox_Auto,
   222  	"hellofirefox_55":        &utls.HelloFirefox_55,
   223  	"hellofirefox_56":        &utls.HelloFirefox_56,
   224  	"hellofirefox_63":        &utls.HelloFirefox_63,
   225  	"hellofirefox_65":        &utls.HelloFirefox_65,
   226  	"hellochrome_auto":       &utls.HelloChrome_Auto,
   227  	"hellochrome_58":         &utls.HelloChrome_58,
   228  	"hellochrome_62":         &utls.HelloChrome_62,
   229  	"hellochrome_70":         &utls.HelloChrome_70,
   230  	"hellochrome_72":         &utls.HelloChrome_72,
   231  	"helloios_auto":          &utls.HelloIOS_Auto,
   232  	"helloios_11_1":          &utls.HelloIOS_11_1,
   233  	"helloios_12_1":          &utls.HelloIOS_12_1,
   234  	"helloandroid_11_okhttp": &utls.HelloAndroid_11_OkHttp,
   235  	"helloedge_auto":         &utls.HelloEdge_Auto,
   236  	"hellosafari_auto":       &utls.HelloSafari_Auto,
   237  	"hello360_auto":          &utls.Hello360_Auto,
   238  	"hello360_7_5":           &utls.Hello360_7_5,
   239  	"helloqq_auto":           &utls.HelloQQ_Auto,
   240  }