提问人:user4779 提问时间:11/18/2023 最后编辑:user4779 更新时间:11/19/2023 访问量:53
使用 httpClient 但从未通过邮递员进行 rdap 响应的某些 Internet 连接出现奇怪故障
Strange failure from some Internet connections for rdap responses using httpClient, but never through postman
问:
我正在尝试编写一个应用程序,该应用程序将允许位于世界任何地方的任何用户根据此处的官方 rdap 服务器列表获得任何域名的相应 tld 的 rdap 响应:https://data.iana.org/rdap/dns.json,同时使用他们自己的本地互联网连接。
然而,奇怪的事情正在发生。使用我的本地 Internet 连接(目前位于东南亚)时,以下 google.com 查询(在 .com 的相应 rdap 服务器上,即 https://rdap.verisign.com/com/v1/)将在大约 80% 的时间内失败:
open System.Net.Http
let httpClient = new HttpClient()
let resp = httpClient.GetAsync($"https://rdap.verisign.com/com/v1/domain/google.com").Result
resp
以下是收到的错误:
System.AggregateException: One or more errors occurred. (The SSL connection could not be established, see inner exception.)
---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host..
---> System.Net.Sockets.SocketException (10054): An existing connection was forcibly closed by the remote host.
--- End of inner exception stack trace ---
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](CancellationToken cancellationToken)
at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
at System.Net.Security.SslStream.ReceiveBlobAsync[TIOAdapter](CancellationToken cancellationToken)
at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(QueueItem queueItem)
at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.HttpConnectionWaiter`1.WaitForConnectionAsync(Boolean async, CancellationToken requestCancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at FSI_0138.makeRequestWithHeaders(String url)
at <StartupCode$FSI_0138>.$FSI_0138.main@()
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at FSI_0138.makeRequestWithHeaders(String url)
at <StartupCode$FSI_0138>.$FSI_0138.main@()
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
这没什么好玩的,可以很容易地用一些本地ISP问题来解释,对吧?
这永远不会失败。有趣!
因此,我想到了在 Wireshark 中捕获数据包。下面显示了使用 httpClient 从 F# 进行的通信(我的真实 IP 已替换为“myip”):
95 2.307971 myip rdap-1.verisigndns.com TCP 86 4 63296 → 443 [SYN] Seq=0 Win=64952 Len=0 MSS=1412 WS=256 SACK_PERM=1
106 2.550129 rdap-1.verisigndns.com myip TCP 78 4 443 → 63296 [SYN, ACK] Seq=0 Ack=1 Win=1240 Len=0 MSS=1240
107 2.550336 myip rdap-1.verisigndns.com TCP 74 4 63296 → 443 [ACK] Seq=1 Ack=1 Win=64952 Len=0
108 2.550804 myip rdap-1.verisigndns.com TLSv1.2 253 4 Client Hello
118 3.112553 myip rdap-1.verisigndns.com TCP 253 4 [TCP Retransmission] 63296 → 443 [PSH, ACK] Seq=1 Ack=1 Win=64952 Len=179
134 3.832227 myip rdap-1.verisigndns.com TCP 253 4 [TCP Retransmission] 63296 → 443 [PSH, ACK] Seq=1 Ack=1 Win=64952 Len=179
159 5.264697 myip rdap-1.verisigndns.com TCP 253 4 [TCP Retransmission] 63296 → 443 [PSH, ACK] Seq=1 Ack=1 Win=64952 Len=179
215 7.797828 rdap-1.verisigndns.com myip TCP 74 4 443 → 63296 [RST, ACK] Seq=1 Ack=1 Win=24000 Len=0
可以看出,在握手成功后,rdap服务器似乎忽略了我的客户端请求。
以下是 postman 中的网络流量,它有效:
19193 526.178083 myip rdap.verisign.com TCP 66 98 53194 → 443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
19199 526.406075 rdap.verisign.com myip TCP 58 98 443 → 53194 [SYN, ACK] Seq=0 Ack=1 Win=1480 Len=0 MSS=1412
19200 526.406368 myip rdap.verisign.com TCP 54 98 53194 → 443 [ACK] Seq=1 Ack=1 Win=64240 Len=0
19201 526.407322 myip rdap.verisign.com TLSv1.3 571 98 Client Hello
19208 526.638776 rdap.verisign.com myip TLSv1.3 1454 98 Server Hello, Change Cipher Spec, Application Data
19209 526.639594 rdap.verisign.com myip TCP 1454 98 443 → 53194 [ACK] Seq=1401 Ack=518 Win=28944 Len=1400 [TCP segment of a reassembled PDU]
19210 526.639648 myip rdap.verisign.com TCP 54 98 53194 → 443 [ACK] Seq=518 Ack=2801 Win=64952 Len=0
19211 526.640264 rdap.verisign.com myip TLSv1.3 1003 98 Application Data, Application Data, Application Data
19212 526.641121 myip rdap.verisign.com TLSv1.3 368 98 Change Cipher Spec, Application Data, Application Data
19213 526.645057 rdap.verisign.com myip TCP 54 98 443 → 53194 [ACK] Seq=1 Ack=518 Win=28944 Len=0
19214 526.645082 myip rdap.verisign.com TCP 54 98 [TCP Dup ACK 19212#1] 53194 → 443 [ACK] Seq=832 Ack=3750 Win=64003 Len=0
19219 526.868863 rdap.verisign.com myip TLSv1.3 293 98 Application Data
19220 526.870003 rdap.verisign.com myip TLSv1.3 1454 98 Application Data
19221 526.870305 myip rdap.verisign.com TCP 54 98 53194 → 443 [ACK] Seq=832 Ack=5389 Win=64952 Len=0
19222 526.870775 rdap.verisign.com myip TCP 1454 98 443 → 53194 [ACK] Seq=5389 Ack=832 Win=30016 Len=1400 [TCP segment of a reassembled PDU]
19223 526.871672 rdap.verisign.com myip TLSv1.3 127 98 Application Data
19224 526.871895 myip rdap.verisign.com TCP 54 98 53194 → 443 [ACK] Seq=832 Ack=6863 Win=64952 Len=0
19225 526.878734 myip rdap.verisign.com TCP 54 98 [TCP Previous segment not captured] 53194 → 443 [FIN, ACK] Seq=856 Ack=6863 Win=64952 Len=0
19226 526.878795 myip rdap.verisign.com TCP 78 98 [TCP Out-Of-Order] 53194 → 443 [PSH, ACK] Seq=832 Ack=6863 Win=64952 Len=24
19231 527.125064 rdap.verisign.com myip TCP 60 98 [TCP Dup ACK 19219#1] 443 → 53194 [ACK] Seq=6863 Ack=832 Win=300
所以我认为也许出于某种原因,rdap 服务器拒绝了来自 .NET httpClient 的流量,而不是 postman(尽管这当然没有意义,因为 rdap 服务应该期望其大部分请求来自自动源,而不是来自 postman 的手动请求)。我尝试手动设置标头以在 .NET 中复制邮递员:
open System.Net.Http
open System.Net.Http.Headers
let makeRequestWithHeaders (url: string) =
let httpClient = new HttpClient()
// Add headers similar to what Postman sends
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("PostmanRuntime/7.26.10")
httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json")
httpClient.DefaultRequestHeaders.CacheControl <- CacheControlHeaderValue.Parse("no-cache")
let response = httpClient.GetAsync(url).Result
response
let url = "https://rdap.verisign.com/com/v1/domain/google.com"
let resp = makeRequestWithHeaders url
resp
然而,没有任何变化(仍然只有~20%的成功率)。
现在事情变得非常奇怪。我连接到美国 VPN 服务器(通过 NordVPN),现在 .NET 代码 100% 可以正常工作,没有任何故障(当然,邮递员仍然可以工作)。
但是,如果我连接到某些区域 VPN 服务器,则会遇到类似的问题,但针对不同的 RDAP 服务器!例如,如果通过 NordVPN 连接到阿根廷 VPN,那么 https://rdap.verisign.com/com/v1/domain/google.com 就可以正常工作,但是例如 .limo 的官方 rdap 服务器会失败 (https://rdap.identitydigital.services/rdap/)。下面是失败的 .NET 代码的示例:
open System.Net.Http
let httpClient = new HttpClient()
let resp = httpClient.GetAsync($"https://rdap.identitydigital.services/rdap/domain/virginia.limo/").Result
resp
(仅在阿根廷 nord VPN 服务器上失败,而不是从任何其他服务器或我的本地互联网连接)。
因此,我对RDAP官方服务器提出了以下观察:
- 我的本地互联网连接可以在 .NET 和 Postman 中查询 .limo 域,但只能从 postman(而不是 .NET)中查询 .com 域
- NordVPN Argentina服务器可以在.NET和Postman中查询.com域,但只能从postman(而不是.NET)中查询.limo域
- NordVPN US 服务器可以很好地查询所有扩展/域,而不会在 .NET 和 postman 中遇到故障。
这一系列令人困惑的事实排除了很多合理的原因,例如httpClient,SSL协议等问题,因为如果是其中任何一个,那么它肯定总是会失败,并且切换到不同的IP和地理位置不应该解决它吗?
那么,如果某些 RDAP 服务器来自 httpClient,某些 RDAP 服务器是否出于某种原因有时会拒绝来自某些地理位置的请求,但如果它们来自 postman,则永远不会拒绝?为什么它不是相同的RDAP服务器,例如:官方.com服务器接受阿根廷流量罚款,但有时拒绝我的本地IP,而官方.limo rdap服务器接受我的本地流量罚款,但有时拒绝阿根廷流量。
这非常令人困惑,我正在寻求任何关于这里可能是什么问题的反馈?如果这真的是不同RDAP服务器和它们的一些奇怪策略的错误,那么我可以将其归咎于我的控制之外,但我想在得出结论之前确保情况确实如此。
警告:
- 这与呼叫无关。GetAsync 上的结果,用于说明目的
- 我的本地连接和阿根廷 nordVPN 服务器都可以直接查询网站(从 .NET 和 postman),只有在与官方 rdap 服务器交互时才会遇到这个问题。
更新(2023 年 11 月 19 日)- 2 天后
我今天的本地互联网连接对于所有请求都正常工作,就像它连接到 NordVPN US VPN 一样,但是阿根廷 VPN 在 .limo 上仍然失败
这越来越像第三方网络问题,但我不明白在哪里、如何或为什么
答: 暂无答案
评论