IPv6 Support
seeder opened this issue · comments
Hi,
I see that you have "excluded" IPv6 support. Which is really bad.
When I've used :: as bind host in your example,
INTERFACES = [
[:udp, "::", 5300],
[:tcp, "::", 5300]
]
it gave me this error :
Errno::EAFNOSUPPORT: Address family not supported by protocol - bind(2) for "::" port 5300
Quick look at rubydns-0.9.1/lib/rubydns/handler.rb
shows @126
def initialize(server, host, port)
socket = UDPSocket.new
socket.bind(host, port)
super(server, socket)
end
now changing line 127 to :
socket = UDPSocket.new Socket::AF_INET6
Gets it running like a charm :
ruby example.rb
I, [2014-12-13T03:05:39.921812 #16965] INFO -- : Starting RubyDNS server (v0.9.1)...
I, [2014-12-13T03:05:39.921953 #16965] INFO -- : <> Listening on udp::::5300
I, [2014-12-13T03:05:39.923565 #16965] INFO -- : <> Listening on tcp::::5300
with netstat confirming opening right sockets:
udp6 0 0 :::5300 :::* 16965/ruby
tcp6 0 0 :::5300 :::* LISTEN 16965/ruby
and dig confirming it works as expected :
dig @localhost -p 5300 test.mydomain.org
; <<>> DiG 9.9.5-3ubuntu0.1-Ubuntu <<>> @localhost -p 5300 test.mydomain.org
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37126
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;test.mydomain.org. IN A
;; ANSWER SECTION:
test.mydomain.org. 86400 IN A 10.0.0.80
;; Query time: 2 msec
;; SERVER: ::1#5300(::1)
with that little change you can have it correctly listening on IPv6 sockets, they are brilliant, because on most systems they also enable IPv4 listening :
same example script running, just query over legacy IPv4
dig -4 @localhost -p 5300 test.mydomain.org
; <<>> DiG 9.9.5-3ubuntu0.1-Ubuntu <<>> -4 @localhost -p 5300 test.mydomain.org
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55503
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;test.mydomain.org. IN A
;; ANSWER SECTION:
test.mydomain.org. 86400 IN A 10.0.0.80
;; Query time: 1 msec
;; SERVER: 127.0.0.1#5300(127.0.0.1)
So the only thing missing would be distinguishing between UDP sockets between IPv6 and IPv4 host, which is darn simple with a regex or IPAddr or even, pass it as an attribute so we can decide.
Thanks for the awesome bug report.
I'm surprised you need to specify the constant Socket::AF_INET6
, I thought with the correct address (i.e. ::
) it would work as expected.
Granted, this has clearly never been tested, so thanks for the thorough review of the current situation.
What solution do you think makes the most sense?
Whatever solution we choose, clearly we should write some specs for it to ensure IPv6 support is well maintained.
If we forgo using "DNS" hostnames it's as simple as 'ipaddr' ( included in stdlib ) can tell you what to use
example (host
is a variable containing IPv4/IPv6 address) :
require 'ipaddr'
host_address = IPAddr.new host
family = host_address.ipv6? ? Socket::AF_INET6 : Socket::AF_INET
socket = UDPSocket.new family
Okay, so we could make a wrapper, e.g.
def network_address(address)
if IPAddr === address
return address
else
return IPAddr.new(address)
end
end
Then in the code for the socket use the code you proposed?
Yup, looks good :)
Okay just release 0.9.2 which fixes this issue.