transport.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package tlsfingerprint
  2. import (
  3. "bufio"
  4. "context"
  5. "crypto/tls"
  6. "crypto/x509"
  7. "encoding/base64"
  8. "fmt"
  9. "net"
  10. "net/http"
  11. "net/url"
  12. "strings"
  13. "time"
  14. "golang.org/x/net/proxy"
  15. )
  16. // TransportOptions configures transport plumbing, not the account fingerprint.
  17. // Nil certificate pools use the system trust store. Certificate verification
  18. // cannot be disabled; local tests may supply a test CA without weakening TLS.
  19. type TransportOptions struct {
  20. DialContext func(context.Context, string, string) (net.Conn, error)
  21. RootCAs *x509.CertPool
  22. ProxyRootCAs *x509.CertPool
  23. HandshakeTimeout time.Duration
  24. }
  25. // NewHTTPTransport returns a native HTTP/1.1 transport that also supports a
  26. // WebSocket 101 upgrade. No response-body wrapper is used. Plain HTTP and
  27. // unsupported proxy/protocol combinations fail instead of bypassing TLS.
  28. func NewHTTPTransport(profile *Profile, proxyURL *url.URL, opts TransportOptions) (*http.Transport, error) {
  29. t := &http.Transport{MaxIdleConns: 100, MaxIdleConnsPerHost: 10, IdleConnTimeout: 90 * time.Second}
  30. if err := ConfigureTransport(t, profile, proxyURL, opts); err != nil {
  31. return nil, err
  32. }
  33. return t, nil
  34. }
  35. // ConfigureTransport installs the same verified uTLS path for HTTP and WSS.
  36. // Pool sizing and response timeouts already set on t are retained.
  37. func ConfigureTransport(t *http.Transport, profile *Profile, proxyURL *url.URL, opts TransportOptions) error {
  38. if t == nil || profile == nil {
  39. return fmt.Errorf("TLS fingerprint transport requires a transport and profile")
  40. }
  41. for _, protocol := range profile.ALPNProtocols {
  42. if protocol != "http/1.1" {
  43. return fmt.Errorf("TLS fingerprint transport does not support ALPN %q", protocol)
  44. }
  45. }
  46. dial, err := configuredTLSDialer(profile, proxyURL, opts)
  47. if err != nil {
  48. return err
  49. }
  50. // net/http cannot infer negotiated HTTP/2 from a *utls.UConn. Advertising
  51. // h2 and then writing HTTP/1.1 would corrupt the connection.
  52. t.ForceAttemptHTTP2 = false
  53. t.TLSNextProto = make(map[string]func(string, *tls.Conn) http.RoundTripper)
  54. t.Proxy = nil // CONNECT is performed by the dialer, including HTTPS proxies.
  55. t.DialTLS = nil
  56. t.TLSClientConfig = &tls.Config{MinVersion: tls.VersionTLS12, NextProtos: []string{"http/1.1"}}
  57. t.DialContext = func(context.Context, string, string) (net.Conn, error) {
  58. return nil, fmt.Errorf("TLS fingerprint transport requires HTTPS or WSS")
  59. }
  60. t.DialTLSContext = dial
  61. return nil
  62. }
  63. func configuredTLSDialer(profile *Profile, proxyURL *url.URL, opts TransportOptions) (func(context.Context, string, string) (net.Conn, error), error) {
  64. p := profile.Clone()
  65. if opts.HandshakeTimeout <= 0 {
  66. opts.HandshakeTimeout = 10 * time.Second
  67. }
  68. if opts.DialContext == nil {
  69. opts.DialContext = (&net.Dialer{Timeout: 10 * time.Second, KeepAlive: 30 * time.Second}).DialContext
  70. }
  71. if opts.RootCAs != nil {
  72. opts.RootCAs = opts.RootCAs.Clone()
  73. }
  74. if opts.ProxyRootCAs != nil {
  75. opts.ProxyRootCAs = opts.ProxyRootCAs.Clone()
  76. }
  77. var proxyCopy *url.URL
  78. if proxyURL != nil {
  79. purl := *proxyURL
  80. purl.Scheme = strings.ToLower(purl.Scheme)
  81. if purl.Hostname() == "" {
  82. return nil, fmt.Errorf("TLS fingerprint proxy host is missing")
  83. }
  84. switch purl.Scheme {
  85. case "http", "https", "socks5", "socks5h":
  86. default:
  87. return nil, fmt.Errorf("unsupported TLS fingerprint proxy scheme %q", purl.Scheme)
  88. }
  89. proxyCopy = &purl
  90. }
  91. return func(ctx context.Context, network, addr string) (net.Conn, error) {
  92. ctx, cancel := context.WithTimeout(ctx, opts.HandshakeTimeout)
  93. defer cancel()
  94. conn, err := openTunnel(ctx, network, addr, proxyCopy, opts)
  95. if err != nil {
  96. return nil, err
  97. }
  98. return performTLSHandshakeWithRoots(ctx, conn, p, addr, opts.RootCAs)
  99. }, nil
  100. }
  101. type contextProxyDialer struct {
  102. ctx context.Context
  103. dial func(context.Context, string, string) (net.Conn, error)
  104. }
  105. func (d contextProxyDialer) Dial(network, addr string) (net.Conn, error) {
  106. return d.dial(d.ctx, network, addr)
  107. }
  108. func (d contextProxyDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
  109. return d.dial(ctx, network, addr)
  110. }
  111. func openTunnel(ctx context.Context, network, addr string, proxyURL *url.URL, opts TransportOptions) (net.Conn, error) {
  112. if proxyURL == nil {
  113. return opts.DialContext(ctx, network, addr)
  114. }
  115. proxyAddr := proxyURL.Host
  116. if proxyURL.Port() == "" {
  117. port := "1080"
  118. if proxyURL.Scheme == "http" {
  119. port = "80"
  120. } else if proxyURL.Scheme == "https" {
  121. port = "443"
  122. }
  123. proxyAddr = net.JoinHostPort(proxyURL.Hostname(), port)
  124. }
  125. if proxyURL.Scheme == "socks5" || proxyURL.Scheme == "socks5h" {
  126. var auth *proxy.Auth
  127. if proxyURL.User != nil {
  128. password, _ := proxyURL.User.Password()
  129. auth = &proxy.Auth{User: proxyURL.User.Username(), Password: password}
  130. }
  131. dialer, err := proxy.SOCKS5(network, proxyAddr, auth, contextProxyDialer{ctx: ctx, dial: opts.DialContext})
  132. if err != nil {
  133. return nil, fmt.Errorf("create SOCKS proxy: %w", err)
  134. }
  135. contextDialer, ok := dialer.(proxy.ContextDialer)
  136. if !ok {
  137. return nil, fmt.Errorf("SOCKS proxy does not support context cancellation")
  138. }
  139. return contextDialer.DialContext(ctx, network, addr)
  140. }
  141. conn, err := opts.DialContext(ctx, network, proxyAddr)
  142. if err != nil {
  143. return nil, fmt.Errorf("connect to proxy: %w", err)
  144. }
  145. complete := false
  146. defer func() {
  147. if !complete {
  148. _ = conn.Close()
  149. }
  150. }()
  151. // CONNECT reads/writes must obey cancellation even when no deadline was
  152. // supplied by the request. configuredTLSDialer always supplies a timeout.
  153. rawConn := conn
  154. stop := context.AfterFunc(ctx, func() { _ = rawConn.Close() })
  155. defer stop()
  156. if proxyURL.Scheme == "https" {
  157. outer := tls.Client(conn, &tls.Config{
  158. ServerName: proxyURL.Hostname(), RootCAs: opts.ProxyRootCAs,
  159. MinVersion: tls.VersionTLS12, NextProtos: []string{"http/1.1"},
  160. })
  161. if err := outer.HandshakeContext(ctx); err != nil {
  162. return nil, fmt.Errorf("HTTPS proxy TLS handshake: %w", err)
  163. }
  164. conn = outer
  165. }
  166. req := &http.Request{Method: http.MethodConnect, URL: &url.URL{Opaque: addr}, Host: addr, Header: make(http.Header)}
  167. if proxyURL.User != nil {
  168. password, _ := proxyURL.User.Password()
  169. value := base64.StdEncoding.EncodeToString([]byte(proxyURL.User.Username() + ":" + password))
  170. req.Header.Set("Proxy-Authorization", "Basic "+value)
  171. }
  172. if err := req.Write(conn); err != nil {
  173. return nil, fmt.Errorf("write proxy CONNECT: %w", err)
  174. }
  175. reader := bufio.NewReader(conn)
  176. resp, err := http.ReadResponse(reader, req)
  177. if err != nil {
  178. return nil, fmt.Errorf("read proxy CONNECT: %w", err)
  179. }
  180. if resp.StatusCode != http.StatusOK {
  181. return nil, fmt.Errorf("proxy CONNECT returned HTTP %d", resp.StatusCode)
  182. }
  183. // A successful CONNECT transfers ownership of the socket to the tunnel.
  184. // Closing its response body would close the tunnel before TLS starts.
  185. if reader.Buffered() > 0 {
  186. conn = &bufferedTunnelConn{Conn: conn, reader: reader}
  187. }
  188. complete = true
  189. return conn, nil
  190. }
  191. type bufferedTunnelConn struct {
  192. net.Conn
  193. reader *bufio.Reader
  194. }
  195. func (c *bufferedTunnelConn) Read(p []byte) (int, error) { return c.reader.Read(p) }