HttpClientHanlder.cs 5.7 KB

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