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

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
Closed

Comments

@jsvd
Copy link
Contributor

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
Copy link
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
Copy link
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
Copy link
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
Copy link
Contributor Author

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
Copy link
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 as completed Mar 26, 2018
@jsvd
Copy link
Contributor Author

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 👍

avgerin0s added a commit to skroutz/logstash-input-gelf that referenced this issue Apr 13, 2021
This code path used to break for IPv6 addresses with
`SocketError (getaddrinfo: Address family for hostname not supported)`
because it was using `Socket::AF_INET` for every input.

This is related to a JRuby bug[1] that is marked as `wontfix`.

With the new implementation the address family is being retrieved by
`Socket.getaddrinfo` and supports both IPv4 and IPv6 addresses.

[1]: jruby/jruby#5112
avgerin0s added a commit to skroutz/logstash-input-gelf that referenced this issue Apr 13, 2021
This code path used to break for IPv6 addresses with
`SocketError (getaddrinfo: Address family for hostname not supported)`
because it was using `Socket::AF_INET` for every input.

This is related to a JRuby bug[1] that is marked as `wontfix`.

With the new implementation the address family is being retrieved by
`Socket.getaddrinfo` and supports both IPv4 and IPv6 addresses.

[1]: jruby/jruby#5112
avgerin0s added a commit to skroutz/logstash-output-statsd that referenced this issue Apr 20, 2021
This fix is related to a known JRuby bug[1] for UDPSocket
initialization.

`statsd-ruby v1.5.0` handles this issue correctly and is compatible with
the `logstash-statsd-output` plugin

[1]: jruby/jruby#5112
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants