alexdalitz / dnsruby

Dnsruby is a feature-complete DNS(SEC) client for Ruby, as used by many of the world's largest DNS registries and the OpenDNSSEC project
Other
194 stars 77 forks source link

Hash results in non-distinct value when address and rdata are the same value #185

Closed rwstauner closed 1 year ago

rwstauner commented 1 year ago

For a call to getresources(domain, "AAAA") i noticed all the items were resulting in the same .hash.

I'm not very familiar with this library, but i noticed that in that response the objects look like this:

 #<Dnsruby::RR::IN::AAAA:0x0000000103f1c490
  @address=#<Dnsruby::IPv6 ...>,
  @klass=IN,
  @name=#<Dnsruby::Name: ...>,
  @rdata=#<Dnsruby::IPv6 ...>,
  @ttl=223,
  @type=AAAA>

compared to just instantiating an object:

*irb /main:073:0:> Dnsruby::RR::IN::AAAA.new("::2")
 => #<Dnsruby::RR::IN::AAAA:0x0000000103e9ae18 @address=#<Dnsruby::IPv6 ::2>, @rdata="::2">

In the latter the rdata is a string and the address is an object. In the former, the rdata is also an object. Since it has the same value as the address object, the exclusive-or in the hash method results in zero:

*irb /main:077:0:> ["::1", "::2"].map { |addr| Dnsruby::IPv6.create(addr) }.map { |ip| Dnsruby::RR::IN::AAAA.new(ip) }.map { |x| {object: x, hash: x.hash} }
 =>
[{:object=>
   #<Dnsruby::RR::IN::AAAA:0x0000000103b385e0
    @address=#<Dnsruby::IPv6 ::1>,
    @rdata=#<Dnsruby::IPv6 ::1>>,
  :hash=>0},
 {:object=>
   #<Dnsruby::RR::IN::AAAA:0x0000000103b38590
    @address=#<Dnsruby::IPv6 ::2>,
    @rdata=#<Dnsruby::IPv6 ::2>>,
  :hash=>0}]

I'm not sure if rdata should be a string, or if the hash method should account for seen values, or one of those instance variables should be dropped from the calculation. Also not sure what other resource types may be affected.

alexdalitz commented 1 year ago

Maybe try :

RR::IN::AAAA.new.from_string("::1”);

?

On 13 Mar 2023, at 17:00, Randy Stauner @.***> wrote:

For a call to getresources(domain, "AAAA") i noticed all the items were resulting in the same .hash.

I'm not very familiar with this library, but i noticed that in that response the objects look like this:

<Dnsruby::RR::IN::AAAA:0x0000000103f1c490

@address=#<Dnsruby::IPv6 ...>, @klass=IN, @name=#<Dnsruby::Name: ...>, @rdata=#<Dnsruby::IPv6 ...>, @ttl=223, @type=AAAA> compared to just instantiating an object:

*irb /main:073:0:> Dnsruby::RR::IN::AAAA.new("::2") => #<Dnsruby::RR::IN::AAAA:0x0000000103e9ae18 @address=#, @rdata="::2"> In the latter the rdata is a string and the address is an object. In the former, the rdata is also an object. Since it has the same value as the address object, the exclusive-or in the hash method results in zero:

*irb /main:077:0:> ["::1", "::2"].map { |addr| Dnsruby::IPv6.create(addr) }.map { |ip| Dnsruby::RR::IN::AAAA.new(ip) }.map { |x| {object: x, hash: x.hash} } => [{:object=>

<Dnsruby::RR::IN::AAAA:0x0000000103b385e0

@address=#<Dnsruby::IPv6 ::1>,
@rdata=#<Dnsruby::IPv6 ::1>>,

:hash=>0}, {:object=>

<Dnsruby::RR::IN::AAAA:0x0000000103b38590

@address=#<Dnsruby::IPv6 ::2>,
@rdata=#<Dnsruby::IPv6 ::2>>,

:hash=>0}] I'm not sure if rdata should be a string, or if the hash method should account for seen values, or one of those instance variables should be dropped from the calculation. Also not sure what other resource types may be affected.

— Reply to this email directly, view it on GitHub https://github.com/alexdalitz/dnsruby/issues/185, or unsubscribe https://github.com/notifications/unsubscribe-auth/AB2WFWUK4EDUVBY3Y2AARETW35HBBANCNFSM6AAAAAAVZLB6RU. You are receiving this because you are subscribed to this thread.

keithrbennett commented 1 year ago

This worked for me:

Dnsruby::RR::IN::AAAA.new.from_string("::1");

Alex, your last double quote was some other fancy double quote character:

[10] pry(main)> "::1”".chars.last.bytes => [226, 128, 157]

On Tue, Mar 14, 2023 at 1:41 AM Alex Dalitz @.***> wrote:

Maybe try :

RR::IN::AAAA.new.from_string("::1”);

?

On 13 Mar 2023, at 17:00, Randy Stauner @.***> wrote:

For a call to getresources(domain, "AAAA") i noticed all the items were resulting in the same .hash.

I'm not very familiar with this library, but i noticed that in that response the objects look like this:

<Dnsruby::RR::IN::AAAA:0x0000000103f1c490

@address=#<Dnsruby::IPv6 ...>, @klass=IN, @name=#<Dnsruby::Name: ...>, @rdata=#<Dnsruby::IPv6 ...>, @ttl=223, @type=AAAA> compared to just instantiating an object:

*irb /main:073:0:> Dnsruby::RR::IN::AAAA.new("::2") => #<Dnsruby::RR::IN::AAAA:0x0000000103e9ae18 @address=#<Dnsruby::IPv6 ::2>, @rdata="::2"> In the latter the rdata is a string and the address is an object. In the former, the rdata is also an object. Since it has the same value as the address object, the exclusive-or in the hash method results in zero:

*irb /main:077:0:> ["::1", "::2"].map { |addr| Dnsruby::IPv6.create(addr) }.map { |ip| Dnsruby::RR::IN::AAAA.new(ip) }.map { |x| {object: x, hash: x.hash} } => [{:object=>

<Dnsruby::RR::IN::AAAA:0x0000000103b385e0

@address=#, @rdata=#>, :hash=>0}, {:object=>

<Dnsruby::RR::IN::AAAA:0x0000000103b38590

@address=#, @rdata=#>, :hash=>0}] I'm not sure if rdata should be a string, or if the hash method should account for seen values, or one of those instance variables should be dropped from the calculation. Also not sure what other resource types may be affected.

— Reply to this email directly, view it on GitHub < https://github.com/alexdalitz/dnsruby/issues/185>, or unsubscribe < https://github.com/notifications/unsubscribe-auth/AB2WFWUK4EDUVBY3Y2AARETW35HBBANCNFSM6AAAAAAVZLB6RU . You are receiving this because you are subscribed to this thread.

— Reply to this email directly, view it on GitHub https://github.com/alexdalitz/dnsruby/issues/185#issuecomment-1466609397, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAG56SBJ4ZA5DSXZMKCGPDW35L5DANCNFSM6AAAAAAVZLB6RU . You are receiving this because you are subscribed to this thread.Message ID: @.***>

rwstauner commented 1 year ago

I'm sorry, I wasn't clear. I was attempting to help demonstrate how the bug with .hash seems to be occurring.

I'm not trying to manually construct these classes, I'm just using Dnsruby::DNS#getresources:

irb(main):006:0> Dnsruby::DNS.new.getresources("aaaa.test.r4s6.net", "AAAA").map { |
x| {object: x, hash: x.hash } }
=>
[{:object=>
   #<Dnsruby::RR::IN::AAAA:0x00007fd451ad05c0
    @address=#<Dnsruby::IPv6 ::1>,
    @klass=IN,
    @name=#<Dnsruby::Name: aaaa.test.r4s6.net.>,
    @rdata=#<Dnsruby::IPv6 ::1>,
    @ttl=20,
    @type=AAAA>,
  :hash=>1645861387863113502},
 {:object=>
   #<Dnsruby::RR::IN::AAAA:0x00007fd451ad0520
    @address=#<Dnsruby::IPv6 ::2>,
    @klass=IN,
    @name=#<Dnsruby::Name: aaaa.test.r4s6.net.>,
    @rdata=#<Dnsruby::IPv6 ::2>,
    @ttl=20,
    @type=AAAA>,
  :hash=>1645861387863113502}]

This call to getresources returns 2 different addresses that each produce the same .hash.

alexdalitz commented 1 year ago

I see - thanks!

It turns out that the way the RR hash is being calculated includes the @rdata, as well as the @address. However, in the AAAA record, they are the same thing, so this code :

def hash # :nodoc: vars = (self.instance_variables - @.***).sort vars.inject(0) do |hash_value, var_name| hash_value ^ self.instance_variable_get(var_name).hash end end ...actually negates the @rdata with the @address, leading to both these records having the same hash.

I will push a change which takes the @rdata out of the hash value, which gives you different hashes for these two records.

If you need this in a new gem, please let me know.

Thanks,

Alex.

On 14 Mar 2023, at 14:28, Randy Stauner @.***> wrote:

I'm sorry, I wasn't clear. I was attempting to help demonstrate how the bug with .hash seems to be occurring.

I'm not trying to manually construct these classes, I'm just using Dnsruby::DNS#getresources:

irb(main):006:0> Dnsruby::DNS.new.getresources("aaaa.test.r4s6.net", "AAAA").map { | x| {object: x, hash: x.hash } } => [{:object=>

<Dnsruby::RR::IN::AAAA:0x00007fd451ad05c0

@address=#<Dnsruby::IPv6 ::1>,
@klass=IN,
@name=#<Dnsruby::Name: aaaa.test.r4s6.net.>,
@rdata=#<Dnsruby::IPv6 ::1>,
@ttl=20,
@type=AAAA>,

:hash=>1645861387863113502}, {:object=>

<Dnsruby::RR::IN::AAAA:0x00007fd451ad0520

@address=#<Dnsruby::IPv6 ::2>,
@klass=IN,
@name=#<Dnsruby::Name: aaaa.test.r4s6.net.>,
@rdata=#<Dnsruby::IPv6 ::2>,
@ttl=20,
@type=AAAA>,

:hash=>1645861387863113502}] This call to getresources returns 2 different addresses that each produce the same .hash.

— Reply to this email directly, view it on GitHub https://github.com/alexdalitz/dnsruby/issues/185#issuecomment-1468204397, or unsubscribe https://github.com/notifications/unsubscribe-auth/AB2WFWVYFC5XX6465USPDADW4B6AFANCNFSM6AAAAAAVZLB6RU. You are receiving this because you commented.

rwstauner commented 1 year ago

i don't need it, just noticed the odd behavior. thanks!

alexdalitz commented 1 year ago

This is now fixed in the newly released v1.70.0 - thanks