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

UDPSocket fails to bind to an IPv6 address unless AF_INET6 is explicitly set #5112

Closed
jsvd opened this Issue Mar 26, 2018 · 6 comments

Comments

Projects
None yet
2 participants
@jsvd

jsvd commented Mar 26, 2018

Binding to an UDP socket on an IPv6 address will fail unless an explicit AF_INET6 setting is passed to constructor. This is not required for TCP, only UDP:

% ruby -v
jruby 9.1.14.0 (2.3.3) 2017-11-08 2176f24 Java HotSpot(TM) 64-Bit Server VM 25.111-b14 on 1.8.0_111-b14 +jit [darwin-x86_64]

Test script:

require "socket"

IPv4 = "127.0.0.1"
IPv6 = "::1"
PORT = 9999

begin
  puts "1) binding in IPv4 TCP..."
  s4 = TCPServer.open IPv4, PORT
  puts "=> OK"
rescue => e
  $stderr.puts "#{e.class}: #{e}"
end

begin
  puts "2) binding in IPv6 TCP..."
  s4 = TCPServer.open IPv6, PORT
  puts "=> OK"
rescue => e
  $stderr.puts "#{e.class}: #{e}"
end

begin
  puts "3) binding in IPv4 UDP..."
  s4 = UDPSocket.new
  s4.bind IPv4, PORT
  puts "=> OK"
rescue => e
  $stderr.puts "#{e.class}: #{e}"
end

begin
  puts "4) binding in IPv6 UDP..."
  s4 = UDPSocket.new
  s4.bind IPv6, PORT
  puts "=> OK"
rescue => e
  $stderr.puts "#{e.class}: #{e}"
end

begin
  puts "5) binding in IPv6 UDP w/ AF_INET6..."
  s5 = UDPSocket.new(Socket::AF_INET6)
  s5.bind IPv6, PORT
  puts "=> OK"
rescue => e
  $stderr.puts "#{e.class}: #{e}"
end

Results in:

1) binding in IPv4 TCP...
=> OK
2) binding in IPv6 TCP...
=> OK
3) binding in IPv4 UDP...
=> OK
4) binding in IPv6 UDP...
Java::JavaNioChannels::UnsupportedAddressTypeException:
5) binding in IPv6 UDP w/ AF_INET6...
=> OK
@headius

This comment has been minimized.

Member

headius commented Mar 26, 2018

Well isn't that quirky. Our UDP logic is just a thin layer over the JDK APIs, so it may be the JDK's issue. I'll poke around a bit.

@headius

This comment has been minimized.

Member

headius commented Mar 26, 2018

Ok it turns out that we have roughly the correct behavior, because CRuby also raises an error in this case:

[] ~/projects/jruby $ ruby -v blah.rb
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17]
1) binding in IPv4 TCP...
=> OK
2) binding in IPv6 TCP...
=> OK
3) binding in IPv4 UDP...
=> OK
4) binding in IPv6 UDP...
SocketError: getaddrinfo: nodename nor servname provided, or not known
5) binding in IPv6 UDP w/ AF_INET6...
=> OK

The remaining problem is that we don't raise the right error, so I'll fix that.

@headius

This comment has been minimized.

Member

headius commented Mar 26, 2018

The actual logic in JDK that appears to raise this error looks something like this:

var5 = Net.checkAddress(var1);
if (this.family == StandardProtocolFamily.INET) {
    InetAddress var6 = var5.getAddress();
    if (!(var6 instanceof Inet4Address)) {
        throw new UnsupportedAddressTypeException();
    }
}

So if I understand, it bails out if the default bind address is not an IPv6 address. That is a bit different sequence of events than CRuby, where it errors because getaddrinfo (to get the bits needed for the bind call) rejects the address as not being IPv6 (or something along those lines).

I will fix our logic to throw a SocketError, so that will match. The error message for all causes of the JDK UnsupportedAddressTypeException will be hardcoded to match the CRuby error.

@jsvd

This comment has been minimized.

jsvd commented Mar 26, 2018

🤦‍♂️ I didn't expect this to be an actual expected behaviour. so weird. Thanks for checking it out. Having the same exception in jruby/mri would be nice, but from a developer POV the pattern for UDP will always be:

  1. check if ip is ipv4 or ipv6
  2. use right AF_INET/AF_INET6 in UDPSocket constructor

headius added a commit that referenced this issue Mar 26, 2018

@headius headius added this to the JRuby 9.1.17.0 milestone Mar 26, 2018

@headius

This comment has been minimized.

Member

headius commented Mar 26, 2018

I've fixed the error so it's at least of the right type. I opted to make a new message since this is raised in a slightly different way than the error produced within CRuby's logic. I doubt it will matter.

@jsvd Could you add a spec for this behavior to https://github.com/ruby/spec? That will ensure we don't regress. If you have any objection to the CRuby behavior, I recommend you open an issue with them at https://bugs.ruby-lang.org.

@headius headius closed this Mar 26, 2018

@jsvd

This comment has been minimized.

jsvd commented Mar 26, 2018

Just for reference, the issue on cruby is https://redmine.ruby-lang.org/issues/5525
It was rejected with the workaround: Addrinfo.udp("::1", 2000).bind

@jsvd Could you add a spec for this behavior to https://github.com/ruby/spec?

good idea 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment