From eeb3c2af306b8fa955e08a714691c311efa6dfc9 Mon Sep 17 00:00:00 2001 From: Jeremy Mahieu Date: Mon, 6 Jul 2020 22:52:27 +0200 Subject: [PATCH 1/2] Do DNS resolution before connection attempt --- .../RabbitMQ.Client/client/api/ITcpClient.cs | 2 ++ .../client/impl/SocketFrameHandler.cs | 35 ++++++++++++++----- .../client/impl/TcpClientAdapter.cs | 6 ++++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/projects/RabbitMQ.Client/client/api/ITcpClient.cs b/projects/RabbitMQ.Client/client/api/ITcpClient.cs index d068f04b22..32660cb6e1 100644 --- a/projects/RabbitMQ.Client/client/api/ITcpClient.cs +++ b/projects/RabbitMQ.Client/client/api/ITcpClient.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using System.Net.Sockets; using System.Threading.Tasks; @@ -17,6 +18,7 @@ public interface ITcpClient : IDisposable Socket Client { get; } Task ConnectAsync(string host, int port); + Task ConnectAsync(IPAddress host, int port); NetworkStream GetStream(); diff --git a/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs b/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs index 7d0dcc0695..455d31e116 100644 --- a/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs +++ b/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs @@ -101,21 +101,38 @@ public SocketFrameHandler(AmqpTcpEndpoint endpoint, _channelReader = channel.Reader; _channelWriter = channel.Writer; - if (ShouldTryIPv6(endpoint)) + // Resolve the hostname to know if it's even possible to even try IPv6 + IPAddress[] adds = Dns.GetHostAddresses(endpoint.HostName); + IPAddress ipv6 = TcpClientAdapterHelper.GetMatchingHost(adds, AddressFamily.InterNetworkV6); + + if (ipv6 == default(IPAddress)) + { + if (endpoint.AddressFamily == AddressFamily.InterNetworkV6) + { + throw new ConnectFailureException("Connection failed", new ArgumentException($"No IPv6 address could be resolved for {endpoint.HostName}")); + } + } + else if (ShouldTryIPv6(endpoint)) { try { - _socket = ConnectUsingIPv6(endpoint, socketFactory, connectionTimeout); + _socket = ConnectUsingIPv6(new IPEndPoint(ipv6, endpoint.Port), socketFactory, connectionTimeout); } catch (ConnectFailureException) { + // We resolved to a ipv6 address and tried it but it still didn't connect, try IPv4 _socket = null; } } - if (_socket == null && endpoint.AddressFamily != AddressFamily.InterNetworkV6) + if (_socket == null) { - _socket = ConnectUsingIPv4(endpoint, socketFactory, connectionTimeout); + IPAddress ipv4 = TcpClientAdapterHelper.GetMatchingHost(adds, AddressFamily.InterNetwork); + if (ipv4 == default(IPAddress)) + { + throw new ConnectFailureException("Connection failed", new ArgumentException($"No ip address could be resolved for {endpoint.HostName}")); + } + _socket = ConnectUsingIPv4(new IPEndPoint(ipv4, endpoint.Port), socketFactory, connectionTimeout); } Stream netstream = _socket.GetStream(); @@ -276,21 +293,21 @@ private static bool ShouldTryIPv6(AmqpTcpEndpoint endpoint) return Socket.OSSupportsIPv6 && endpoint.AddressFamily != AddressFamily.InterNetwork; } - private ITcpClient ConnectUsingIPv6(AmqpTcpEndpoint endpoint, + private ITcpClient ConnectUsingIPv6(IPEndPoint endpoint, Func socketFactory, TimeSpan timeout) { return ConnectUsingAddressFamily(endpoint, socketFactory, timeout, AddressFamily.InterNetworkV6); } - private ITcpClient ConnectUsingIPv4(AmqpTcpEndpoint endpoint, + private ITcpClient ConnectUsingIPv4(IPEndPoint endpoint, Func socketFactory, TimeSpan timeout) { return ConnectUsingAddressFamily(endpoint, socketFactory, timeout, AddressFamily.InterNetwork); } - private ITcpClient ConnectUsingAddressFamily(AmqpTcpEndpoint endpoint, + private ITcpClient ConnectUsingAddressFamily(IPEndPoint endpoint, Func socketFactory, TimeSpan timeout, AddressFamily family) { @@ -307,11 +324,11 @@ private ITcpClient ConnectUsingAddressFamily(AmqpTcpEndpoint endpoint, } } - private void ConnectOrFail(ITcpClient socket, AmqpTcpEndpoint endpoint, TimeSpan timeout) + private void ConnectOrFail(ITcpClient socket, IPEndPoint endpoint, TimeSpan timeout) { try { - socket.ConnectAsync(endpoint.HostName, endpoint.Port) + socket.ConnectAsync(endpoint.Address, endpoint.Port) .TimeoutAfter(timeout) .ConfigureAwait(false) // this ensures exceptions aren't wrapped in an AggregateException diff --git a/projects/RabbitMQ.Client/client/impl/TcpClientAdapter.cs b/projects/RabbitMQ.Client/client/impl/TcpClientAdapter.cs index 2cb0eb58cc..f74e6dce67 100644 --- a/projects/RabbitMQ.Client/client/impl/TcpClientAdapter.cs +++ b/projects/RabbitMQ.Client/client/impl/TcpClientAdapter.cs @@ -27,6 +27,12 @@ public virtual async Task ConnectAsync(string host, int port) throw new ArgumentException($"No ip address could be resolved for {host}"); } + await ConnectAsync(ep, port); + } + + public virtual async Task ConnectAsync(IPAddress ep, int port) + { + AssertSocket(); #if NET461 await Task.Run(() => _sock.Connect(ep, port)).ConfigureAwait(false); #else From 4bf811216f03f553461b7c2ca52ad14041808c83 Mon Sep 17 00:00:00 2001 From: Jeremy Mahieu Date: Tue, 7 Jul 2020 09:43:26 +0200 Subject: [PATCH 2/2] Adjust APIApproval test --- projects/Unit/APIApproval.Approve.verified.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/Unit/APIApproval.Approve.verified.txt b/projects/Unit/APIApproval.Approve.verified.txt index b0b6254a1a..615a0580c8 100644 --- a/projects/Unit/APIApproval.Approve.verified.txt +++ b/projects/Unit/APIApproval.Approve.verified.txt @@ -494,6 +494,7 @@ namespace RabbitMQ.Client bool Connected { get; } System.TimeSpan ReceiveTimeout { get; set; } void Close(); + System.Threading.Tasks.Task ConnectAsync(System.Net.IPAddress host, int port); System.Threading.Tasks.Task ConnectAsync(string host, int port); System.Net.Sockets.NetworkStream GetStream(); }