Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nats.Client can't connect to IPv6 server on .net core < 3.0 #272

Closed
SonicGD opened this issue Jul 31, 2019 · 10 comments
Closed

Nats.Client can't connect to IPv6 server on .net core < 3.0 #272

SonicGD opened this issue Jul 31, 2019 · 10 comments
Labels
.NET Core Issues specific to .NET Core

Comments

@SonicGD
Copy link

SonicGD commented Jul 31, 2019

Hello. I'm facing problem with connecting to nats server via ipv6.

var cf = new ConnectionFactory();
var c = cf.CreateConnection("nats://[::1]:4222");

it crashes with exception

Unhandled Exception: NATS.Client.NATSNoServersException: Unable to connect to a server.
   at NATS.Client.Connection.connect() in C:\Users\Sonic\RiderProjects\NatsIPv6\NatsIPv6\NATS.Client\Conn.cs:line 1131
   at NATS.Client.ConnectionFactory.CreateConnection(Options opts) in C:\Users\Sonic\RiderProjects\NatsIPv6\NatsIPv6\NATS.Client\ConnectionFactory.cs:line 171
   at NATS.Client.ConnectionFactory.CreateConnection(String url) in C:\Users\Sonic\RiderProjects\NatsIPv6\NatsIPv6\NATS.Client\ConnectionFactory.cs:line 48
   at NatsIPv6.Program.Main(String[] args) in C:\Users\Sonic\RiderProjects\NatsIPv6\NatsIPv6\Program.cs:line 14

As i understand, problem is here - https://github.com/nats-io/nats.net/blob/master/NATS.Client/Conn.cs#L427. By default TCPClient created with AddressFamily.InterNetwork which is IPv4 only. If i change this line to

client = new TcpClient(AddressFamily.InterNetworkV6);

than it can connect to ipv6 nats without problem.

More interestingly, everything works fine without any modifications on .net core 3.0, cause of this pr - dotnet/corefx#30036.

But i think it should be fixed for 2.2 also. Best way to do it and stay compatible with 3.0 changes is to resolve ip address before tcpclient creation. Something like that:

var ipAddress = Dns.GetHostAddresses(s.url.Host).FirstOrDefault();
if (ipAddress == null)
{
     throw new NATSConnectionException("can't resolve server ip");
}
client = new TcpClient(ipAddress.AddressFamily);
var task = client.ConnectAsync(ipAddress, s.url.Port);

This way it works for ipv4 and ipv6 on both .net core 2.2 and 3.0. If this is correct approach - i can make pr.

@watfordgnf
Copy link
Collaborator

Does the following work on 2.2?

var ipAddresses = Dns.GetHostAddresses(s.url.Host);
if (ipAddresses?.Length == 0)
{
    throw new NATSConnectionException($"Could not resolve host {s.url.Host}");
}
client = new TcpClient();
var task = client.ConnectAsync(ipAddresses, s.url.Port);

This would use all available IP Addresses for the connection.

I don't believe that Dns.GetHostAddresses returns null in the case of a problem, I think it throws an exception.

> System.Net.Dns.GetHostAddresses("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab122.com")
No such host is known
  + System.Net.Dns.GetAddrInfo(string)
  + System.Net.Dns.InternalGetHostByName(string, bool)
  + System.Net.Dns.GetHostAddresses(string)

It does look like it could return an empty array if only IPv6 addresses were available on an IPv4 only machine.

@SonicGD
Copy link
Author

SonicGD commented Aug 1, 2019

No, it doesn't. To get it work we still need to specify family address

var ipAddresses = Dns.GetHostAddresses(s.url.Host);
if (ipAddresses.Length == 0)
{
    throw new NATSConnectionException($"Could not resolve host {s.url.Host}");
}
var ipV4only = ipAddresses.All(addr => addr.AddressFamily == AddressFamily.InterNetwork);
client = new TcpClient(ipV4only ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6);
var task = client.ConnectAsync(ipAddresses, s.url.Port);

@ColinSullivan1
Copy link
Member

Thank you for looking into this, much appreciated. The option above looks feasible. I'm also wondering if we should stick to whatever the .NET framework defaults to, but add an option to override the AddressFamily.

@watfordgnf
Copy link
Collaborator

I'm unable to get the fix working unfortunately, hung up on possibly a toolchain issue, but I'm not sure: dotnet/standard#1431

@tanwarsatya
Copy link

After Windows 10 KB4512508, the client cannot connect to nats server with default options, using .Net core 2.2. It was working before the update and suddenly not able to connect to nats://localhost:4222

@ColinSullivan1
Copy link
Member

ColinSullivan1 commented Aug 15, 2019

This is no good.

imo, there are a few options until the issue is resolved in .NET core... There is the simple way to expose an option to override the default address family. Likely only InterNetwork and InterNetworkV6 will work, but we never know what could be coming up in the future. A drawback is that it will require code changes.

If the default fails, we could keep trying to connect looping through the address families. e.g:
https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket?view=netframework-4.8#examples

The DNS option looks ok, but imo seems fragile (not because of the code), but because it's DNS.

@watfordgnf
Copy link
Collaborator

Looks like because we reference .NET Standard 1.3 and not something later like 1.6, we can't use the non-async versions:

dotnet/standard#1431 (comment)

The API definition for earlier versions of .NET Standard are fixed. In .NET Standard 1.3, the API surface of Dns was limited to this:

public static class Dns
{
	public static Task<IPAddress[]> GetHostAddressesAsync(string hostNameOrAddress);
	public static Task<IPHostEntry> GetHostEntryAsync(IPAddress address);
	public static Task<IPHostEntry> GetHostEntryAsync(string hostNameOrAddress);
	public static string GetHostName();
}

The rest came back in .NET Standard 2.0, which includes the Dns.GetHostAddresses method.

@ColinSullivan1
Copy link
Member

We should be referencing netstandard1.6 in the project files and nuget packaging. Do you see a 1.3 reference someplace?

@watfordgnf
Copy link
Collaborator

watfordgnf commented Aug 21, 2019

You're correct, it is the System.Net.NameResolution which references 1.3. So we would need to reference netstandard2.0 in order to use Dns.GetHostAddresses; though, I'm with you that isn't terribly fun.

@watfordgnf watfordgnf added bug .NET Core Issues specific to .NET Core labels Aug 21, 2019
@ColinSullivan1
Copy link
Member

We are in the process of making updates that will put us in position to workaround whatever changed in .NET. Please be patient...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
.NET Core Issues specific to .NET Core
Projects
None yet
Development

No branches or pull requests

5 participants