github.com/metacubex/mihomo@v1.18.5/component/tls/utls.go (about)

     1  package tls
     2  
     3  import (
     4  	"crypto/tls"
     5  	"net"
     6  
     7  	"github.com/metacubex/mihomo/log"
     8  
     9  	"github.com/mroth/weightedrand/v2"
    10  	utls "github.com/sagernet/utls"
    11  )
    12  
    13  type UConn struct {
    14  	*utls.UConn
    15  }
    16  
    17  type UClientHelloID struct {
    18  	*utls.ClientHelloID
    19  }
    20  
    21  var initRandomFingerprint UClientHelloID
    22  var initUtlsClient string
    23  
    24  func UClient(c net.Conn, config *tls.Config, fingerprint UClientHelloID) *UConn {
    25  	utlsConn := utls.UClient(c, copyConfig(config), utls.ClientHelloID{
    26  		Client:  fingerprint.Client,
    27  		Version: fingerprint.Version,
    28  		Seed:    fingerprint.Seed,
    29  	})
    30  	return &UConn{UConn: utlsConn}
    31  }
    32  
    33  func GetFingerprint(ClientFingerprint string) (UClientHelloID, bool) {
    34  	if ClientFingerprint == "none" {
    35  		return UClientHelloID{}, false
    36  	}
    37  
    38  	if initRandomFingerprint.ClientHelloID == nil {
    39  		initRandomFingerprint, _ = RollFingerprint()
    40  	}
    41  
    42  	if ClientFingerprint == "random" {
    43  		log.Debugln("use initial random HelloID:%s", initRandomFingerprint.Client)
    44  		return initRandomFingerprint, true
    45  	}
    46  
    47  	fingerprint, ok := Fingerprints[ClientFingerprint]
    48  	if ok {
    49  		log.Debugln("use specified fingerprint:%s", fingerprint.Client)
    50  		return fingerprint, ok
    51  	} else {
    52  		log.Warnln("wrong ClientFingerprint:%s", ClientFingerprint)
    53  		return UClientHelloID{}, false
    54  	}
    55  }
    56  
    57  func RollFingerprint() (UClientHelloID, bool) {
    58  	chooser, _ := weightedrand.NewChooser(
    59  		weightedrand.NewChoice("chrome", 6),
    60  		weightedrand.NewChoice("safari", 3),
    61  		weightedrand.NewChoice("ios", 2),
    62  		weightedrand.NewChoice("firefox", 1),
    63  	)
    64  	initClient := chooser.Pick()
    65  	log.Debugln("initial random HelloID:%s", initClient)
    66  	fingerprint, ok := Fingerprints[initClient]
    67  	return fingerprint, ok
    68  }
    69  
    70  var Fingerprints = map[string]UClientHelloID{
    71  	"chrome":                     {&utls.HelloChrome_Auto},
    72  	"chrome_psk":                 {&utls.HelloChrome_100_PSK},
    73  	"chrome_psk_shuffle":         {&utls.HelloChrome_106_Shuffle},
    74  	"chrome_padding_psk_shuffle": {&utls.HelloChrome_114_Padding_PSK_Shuf},
    75  	"chrome_pq":                  {&utls.HelloChrome_115_PQ},
    76  	"chrome_pq_psk":              {&utls.HelloChrome_115_PQ_PSK},
    77  	"firefox":                    {&utls.HelloFirefox_Auto},
    78  	"safari":                     {&utls.HelloSafari_Auto},
    79  	"ios":                        {&utls.HelloIOS_Auto},
    80  	"android":                    {&utls.HelloAndroid_11_OkHttp},
    81  	"edge":                       {&utls.HelloEdge_Auto},
    82  	"360":                        {&utls.Hello360_Auto},
    83  	"qq":                         {&utls.HelloQQ_Auto},
    84  	"random":                     {nil},
    85  	"randomized":                 {nil},
    86  }
    87  
    88  func init() {
    89  	weights := utls.DefaultWeights
    90  	weights.TLSVersMax_Set_VersionTLS13 = 1
    91  	weights.FirstKeyShare_Set_CurveP256 = 0
    92  	randomized := utls.HelloRandomized
    93  	randomized.Seed, _ = utls.NewPRNGSeed()
    94  	randomized.Weights = &weights
    95  	Fingerprints["randomized"] = UClientHelloID{&randomized}
    96  }
    97  
    98  func copyConfig(c *tls.Config) *utls.Config {
    99  	return &utls.Config{
   100  		RootCAs:               c.RootCAs,
   101  		ServerName:            c.ServerName,
   102  		InsecureSkipVerify:    c.InsecureSkipVerify,
   103  		VerifyPeerCertificate: c.VerifyPeerCertificate,
   104  	}
   105  }
   106  
   107  // BuildWebsocketHandshakeState it will only send http/1.1 in its ALPN.
   108  // Copy from https://github.com/XTLS/Xray-core/blob/main/transport/internet/tls/tls.go
   109  func (c *UConn) BuildWebsocketHandshakeState() error {
   110  	// Build the handshake state. This will apply every variable of the TLS of the
   111  	// fingerprint in the UConn
   112  	if err := c.BuildHandshakeState(); err != nil {
   113  		return err
   114  	}
   115  	// Iterate over extensions and check for utls.ALPNExtension
   116  	hasALPNExtension := false
   117  	for _, extension := range c.Extensions {
   118  		if alpn, ok := extension.(*utls.ALPNExtension); ok {
   119  			hasALPNExtension = true
   120  			alpn.AlpnProtocols = []string{"http/1.1"}
   121  			break
   122  		}
   123  	}
   124  	if !hasALPNExtension { // Append extension if doesn't exists
   125  		c.Extensions = append(c.Extensions, &utls.ALPNExtension{AlpnProtocols: []string{"http/1.1"}})
   126  	}
   127  	// Rebuild the client hello
   128  	if err := c.BuildHandshakeState(); err != nil {
   129  		return err
   130  	}
   131  	return nil
   132  }
   133  
   134  func SetGlobalUtlsClient(Client string) {
   135  	initUtlsClient = Client
   136  }
   137  
   138  func HaveGlobalFingerprint() bool {
   139  	return len(initUtlsClient) != 0 && initUtlsClient != "none"
   140  }
   141  
   142  func GetGlobalFingerprint() string {
   143  	return initUtlsClient
   144  }