Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Socket.gethostbyname respond with "<any>" and "<broadcast>" hosts #2111

Closed
wants to merge 4 commits into from

3 participants

Sylvain Daubert Dirkjan Bussink Gabor Garami
Sylvain Daubert

Make specs "Socket#gethostbyname returns broadcast address info for ''" and "Socket#gethostbyname returns broadcast address info for ''" pass
See commit messages for details.

sdaubert added some commits
Sylvain Daubert sdaubert Spec "Socket#gethostbyname returns broadcast address info for '<any>'…
…" is OK.

Socket.getaddrinfo is modified to act the same way when +host+ is "" or is
"<any>".
Socket.gethostbyname is modified before returning : addresses array is modified
to return packed-string addresses instead of IPv4 dotted addresses.
a13ece8
Sylvain Daubert sdaubert Make spec "Socket#gethostbyname returns broadcast address info for '<…
…broadcast>'" pass.

Socket::Foreign.getaddrinfo is modified to use "<broadcast>" as "255.255.255.255" (first
+if+ statement).
25fc012
Sylvain Daubert sdaubert Remove fail tag on library/socket/socket/gethostbyname. 20540b1
Dirkjan Bussink
Owner

Few comments, first of all, why was this only added for 1.9 mode? This seems like a feature that should be available also in 1.8, since there are also tags in 1.8 mode for this.

Dirkjan Bussink dbussink commented on the diff
lib/19/socket.rb
@@ -688,6 +690,16 @@ def self.gethostbyname(hostname)
addresses << a[3] if a[4] == family
end
+ addresses.map! do |addr|
Dirkjan Bussink Owner

Could you explain what this code does? Personally I don't find it very readable, maybe there is a way we can make this cleaner / more obvious as to what it does?

This code transforms a dotted IPv4 address into a packed address, as returned by MRI.
First, if addr seems to be an IPv4 address, each address byte is packed into string s. Else addr is returned unchanged.

Now, for packing, i think it should use pack:

$~[1..4].map(:to_i).pack("C*")

instead of

$~[1..4].each { |v| s << v.to_i.chr }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Sylvain Daubert

I plan to add it for 1.8 mode after it will be accepted for 1.9. I am not confident about some modifications, like line 693.

Dirkjan Bussink
Owner

Ah ok, we would prefer to have them in a single pull request then, so the changes can be made together. I suspect the changes will be exactly the same anyway, but we can work on reviewing this change first then if you are more comfortable with that.

I don't really have much time today anymore though, you know, something with 2013 ;). Have a good new year, and I'll take a further look later this week :).

Sylvain Daubert

I agree you about changes on 1.8 version, but I would prefer reviewing this change first.
Thanks.

Sylvain Daubert

As MRI also returns a packed string for IPv6 address, i propose to modify code as:

    addresses.map! do |addr|
      if addr =~ /\./
        # IPv4 address
        addr.split('.').map { |v| v.to_i }.pack('C*')
      elsif addr =~ /:/
        # IPv6 address
        left, right = addr.split('::')
        l = left.split(':')
        r = right.split(':')
        rest = 8 - l.size - r.size
        ary = l + ['0'] * rest + r
        ary.map { |v| v.to_i }.pack('n8')
      else
        # other (?): return addr as is
        addr
      end
Gabor Garami

@sdaubert I worry about your solution. IPv6 address can contain double colon, but it is not a requirement. If IPv6 address is written in fully-expanded form, there is no double colon.

Ohh, and simple shortening:

addr.split('.').map(&:to_i).pack('C*')
Sylvain Daubert

@hron84 you are right. To make this code work, we have to add

right ||= ''

after

left, right = addr.split('::')

I also discovered a bug by testing fully expanded form. We must use

ary.map { |v| v.to_i(16) }.pack('n8')

instead of

ary.map { |v| v.to_i }.pack('n8')
Sylvain Daubert

I added commit 90534f2 for supporting IPv6 addresses (and also dotted format for IPv4-mapped and IPv4-compatible IPv6 addresses).

Dirkjan Bussink
Owner

Ok, there are two concerns here. The one with handling and and the fact that Socket.gethostbyname doesn't return the addresses encoded in the same way as MRI. We should not be mixing these two issues here probably.

For the second issue, this is not the way we should approach fixing it. Doing this with manual regexp parsing is error prone and we should be using SockAddr_In and probably add the SockAddr_In6 struct for that. Those are the canonical structures that handle parsing like this, we shouldn't be copying that logic in other places.

This is not really a trivial change and probably requires much more work than this. What I suggest for this pull request, is changing the spec so it doesn't depend on matching the whole result of Socket.gethostbyname, but just on the first entry of the array that gethostbyname returns and specify that has to be "0.0.0.0" for and "255.255.255.255" for .

The formatting of the address as the last entry should be specced separate, so we can also tackle that as a separate issue.

Sylvain Daubert

So, I make a new pull request for handling 'any' and 'broadcast' and for modifying specs to test only the first item of returned array. This pull request will handle all ruby versions.
I will try to fill another pull request later about handling last item in returned array, with specs.
Is that right ?

Dirkjan Bussink
Owner

Right, we should handle this first case first. The second part will be a lot more complicated I guess though, I've tried to see what needed to be done, and it's quite a significant effort and probably touches a bunch of more places, like FFI struct generation etc.

Sylvain Daubert

For the first case, see new pull request #2115.

Sylvain Daubert

For the second issue, I propose a new solution in #2125. If #2125 is accepted, then we could close this pull request, i think.

Dirkjan Bussink
Owner

I'm closing this since it has been handled in two separate pull requests.

Dirkjan Bussink dbussink closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 30, 2012
  1. Sylvain Daubert

    Spec "Socket#gethostbyname returns broadcast address info for '<any>'…

    sdaubert authored
    …" is OK.
    
    Socket.getaddrinfo is modified to act the same way when +host+ is "" or is
    "<any>".
    Socket.gethostbyname is modified before returning : addresses array is modified
    to return packed-string addresses instead of IPv4 dotted addresses.
Commits on Dec 31, 2012
  1. Sylvain Daubert

    Make spec "Socket#gethostbyname returns broadcast address info for '<…

    sdaubert authored
    …broadcast>'" pass.
    
    Socket::Foreign.getaddrinfo is modified to use "<broadcast>" as "255.255.255.255" (first
    +if+ statement).
  2. Sylvain Daubert
Commits on Jan 3, 2013
  1. Sylvain Daubert

    Modify end of Socket.gethostbyname to also convert IPv6 addresses into

    sdaubert authored
    packed strings. This code is taken from IPAddr#hton.
This page is out of date. Refresh to see the latest.
30 lib/19/socket.rb
View
@@ -357,8 +357,10 @@ def self.getaddrinfo(host, service = nil, family = 0, socktype = 0, protocol =
hints[:ai_protocol] = protocol
hints[:ai_flags] = flags
- if host && host.empty?
+ if host && (host.empty? || host == '<any>')
host = "0.0.0.0"
+ elsif host == '<broadcast>'
+ host = '255.255.255.255'
end
res_p = FFI::MemoryPointer.new :pointer
@@ -688,6 +690,32 @@ def self.gethostbyname(hostname)
addresses << a[3] if a[4] == family
end
+ addresses.map! do |addr|
Dirkjan Bussink Owner

Could you explain what this code does? Personally I don't find it very readable, maybe there is a way we can make this cleaner / more obvious as to what it does?

This code transforms a dotted IPv4 address into a packed address, as returned by MRI.
First, if addr seems to be an IPv4 address, each address byte is packed into string s. Else addr is returned unchanged.

Now, for packing, i think it should use pack:

$~[1..4].map(:to_i).pack("C*")

instead of

$~[1..4].each { |v| s << v.to_i.chr }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ case addr
+ when /^\d+\.\d+\.\d+\.\d+$/
+ # IPv4 address
+ addr.split('.').map(&:to_i).pack('C*')
+ when /^::(ffff:)?(\d+\.\d+\.\d+\.\d+)$/i
+ # IPv4-mapped and IPv4-compatible IPv6 address
+ constant_bytes = "\x00" * 10
+ if $1
+ constant_bytes += "\xff" * 2
+ else
+ constant_bytes += "\x00" * 2
+ end
+ constant_bytes + $2.split('.').map(&:to_i).pack('C*')
+ else
+ # IPv6 address
+ left, right = addr.split('::')
+ right ||= ''
+ l = left.split(':')
+ r = right.split(':')
+ rest = 8 - l.size - r.size
+ ary = l + ['0'] * rest + r
+ ary.map { |v| v.to_i(16) }.pack('n8')
+ end
+ end
+
[hostname, alternatives.uniq, family] + addresses.uniq
end
2  spec/tags/19/ruby/library/socket/socket/gethostbyname_tags.txt
View
@@ -1,2 +0,0 @@
-fails:Socket#gethostbyname returns broadcast address info for '<broadcast>'
-fails:Socket#gethostbyname returns broadcast address info for '<any>'
Something went wrong with that request. Please try again.