2
0

HttpClientHandler.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. using FastGithub.DomainResolve;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Net.Http;
  8. using System.Net.Security;
  9. using System.Net.Sockets;
  10. using System.Security.Cryptography.X509Certificates;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. namespace FastGithub.Http
  14. {
  15. /// <summary>
  16. /// HttpClientHandler
  17. /// </summary>
  18. class HttpClientHandler : DelegatingHandler
  19. {
  20. private readonly IDomainResolver domainResolver;
  21. /// <summary>
  22. /// HttpClientHandler
  23. /// </summary>
  24. /// <param name="domainResolver"></param>
  25. public HttpClientHandler(IDomainResolver domainResolver)
  26. {
  27. this.domainResolver = domainResolver;
  28. this.InnerHandler = CreateSocketsHttpHandler();
  29. }
  30. /// <summary>
  31. /// 创建转发代理的httpHandler
  32. /// </summary>
  33. /// <returns></returns>
  34. private static SocketsHttpHandler CreateSocketsHttpHandler()
  35. {
  36. return new SocketsHttpHandler
  37. {
  38. Proxy = null,
  39. UseProxy = false,
  40. AllowAutoRedirect = false,
  41. AutomaticDecompression = DecompressionMethods.None,
  42. ConnectCallback = async (context, cancellationToken) =>
  43. {
  44. var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
  45. await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);
  46. var stream = new NetworkStream(socket, ownsSocket: true);
  47. var requestContext = context.InitialRequestMessage.GetRequestContext();
  48. if (requestContext.IsHttps == false)
  49. {
  50. return stream;
  51. }
  52. var sslStream = new SslStream(stream, leaveInnerStreamOpen: false);
  53. await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
  54. {
  55. TargetHost = requestContext.TlsSniPattern.Value,
  56. RemoteCertificateValidationCallback = ValidateServerCertificate
  57. }, cancellationToken);
  58. return sslStream;
  59. bool ValidateServerCertificate(object sender, X509Certificate? cert, X509Chain? chain, SslPolicyErrors errors)
  60. {
  61. if (errors == SslPolicyErrors.RemoteCertificateNameMismatch)
  62. {
  63. if (requestContext.TlsIgnoreNameMismatch == true)
  64. {
  65. return true;
  66. }
  67. var host = requestContext.Host;
  68. var dnsNames = ReadDnsNames(cert);
  69. return dnsNames.Any(dns => IsMatch(dns, host));
  70. }
  71. return errors == SslPolicyErrors.None;
  72. }
  73. }
  74. };
  75. }
  76. /// <summary>
  77. /// 读取使用的DNS名称
  78. /// </summary>
  79. /// <param name="cert"></param>
  80. /// <returns></returns>
  81. private static IEnumerable<string> ReadDnsNames(X509Certificate? cert)
  82. {
  83. if (cert == null)
  84. {
  85. yield break;
  86. }
  87. var parser = new Org.BouncyCastle.X509.X509CertificateParser();
  88. var x509Cert = parser.ReadCertificate(cert.GetRawCertData());
  89. var subjects = x509Cert.GetSubjectAlternativeNames();
  90. foreach (var subject in subjects)
  91. {
  92. if (subject is IList list)
  93. {
  94. if (list.Count >= 2 && list[0] is int nameType && nameType == 2)
  95. {
  96. var dnsName = list[1]?.ToString();
  97. if (dnsName != null)
  98. {
  99. yield return dnsName;
  100. }
  101. }
  102. }
  103. }
  104. }
  105. /// <summary>
  106. /// 比较域名
  107. /// </summary>
  108. /// <param name="dnsName"></param>
  109. /// <param name="host"></param>
  110. /// <returns></returns>
  111. private static bool IsMatch(string dnsName, string? host)
  112. {
  113. if (host == null)
  114. {
  115. return false;
  116. }
  117. if (dnsName == host)
  118. {
  119. return true;
  120. }
  121. if (dnsName[0] == '*')
  122. {
  123. return host.EndsWith(dnsName[1..]);
  124. }
  125. return false;
  126. }
  127. /// <summary>
  128. /// 替换域名为ip
  129. /// </summary>
  130. /// <param name="request"></param>
  131. /// <param name="cancellationToken"></param>
  132. /// <returns></returns>
  133. protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  134. {
  135. var uri = request.RequestUri;
  136. if (uri != null && uri.HostNameType == UriHostNameType.Dns)
  137. {
  138. var address = await this.domainResolver.ResolveAsync(uri.Host, cancellationToken);
  139. var builder = new UriBuilder(uri)
  140. {
  141. Scheme = Uri.UriSchemeHttp,
  142. Host = address.ToString(),
  143. };
  144. request.RequestUri = builder.Uri;
  145. request.Headers.Host = uri.Host;
  146. var context = request.GetRequestContext();
  147. context.TlsSniPattern = context.TlsSniPattern.WithDomain(uri.Host).WithIPAddress(address).WithRandom();
  148. }
  149. return await base.SendAsync(request, cancellationToken);
  150. }
  151. }
  152. }