[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

'example.com' == 'example.com.' => false... is this intended?

Sam Roberts

1/31/2005 2:32:00 AM


Hi Tanaka,

I don't understand why DNS::Name#== requires both to be absolute if one
is.

Is this really necessary/useful? It surprises me.

Also, I have a set of comparison operations for Resolv::DNS::Name.

I copied the style (and docs) from Module/hierarchy comparisons, because
I think there is some similarity.

Comments?

If you will accept, I will send patch and changelog.

Thanks,
Sam

Below is implementation, followed by unit test so you can see behaviour.


# DNS names are hierarchical in a similar sense to ruby classes/modules, and the
# comparison operators are defined similarly to those of Module. A name is
# +<+ another if it is a subdomain.
# www.example.com < example.com # -> true
# example.com < example.com # -> false
# example.com <= example.com # -> true
# com < example.com # -> false
# bar.com < example.com # -> nil
#
# Note that #== does not consider two names equal if they differ in whether
# they are #absolute?, but #equal? considers only the label when comparing
# names.
class Name
def inspect
n = to_s
n << '.' if absolute?
return n
end

def equal?(name)
n = Name.create(name)

@labels == n.to_a
end

def related?(name)
n = Name.create(name)

l = length < n.length ? length : n.length

@labels[-l, l] == n.to_a[-l, l]
end

def lt?(name)
n = Name.create(name)
length > n.length && to_a[-n.length, n.length] == n.to_a
end


# Summary:
# name < other => true, false, or nil
#
# Returns true if +name+ is a subdomain of +other+. Returns
# <code>nil</code> if there's no relationship between the two.
def <(name)
n = Name.create(name)

return nil unless self.related?(n)

lt?(n)
end

# Summary:
# name > other => true, false, or nil
#
# Same as +other < name+, see #<.
def >(name)
n = Name.create(name)

n < self
end

# Summary:
# name <= other => true, false, or nil
#
# Returns true if +name+ is a subdomain of +other+ or is the same as
# +other+. Returns <code>nil</code> if there's no relationship between
# the two.
def <=(name)
n = Name.create(name)
self.equal?(n) || self < n
end

# Summary:
# name >= other => true, false, or nil
#
# Returns true if +name+ is an ancestor of +other+, or the two DNS names
# are the same. Returns <code>nil</code> if there's no relationship
# between the two.
def >=(name)
n = Name.create(name)
self.equal?(n) || self > n
end

# Summary:
# name <=> other => -1, 0, +1, nil
#
# Returns -1 if +name+ is a subdomain of +other+, 0 if
# +name+ is the same as +other+, and +1 if +other+ is a subdomain of
# +name+, or nil if +name+ has no relationship with +other+.
def <=>(name)
n = Name.create(name)

return nil unless self.related?(n)

return -1 if self.lt?(n)
return +1 if n.lt?(self)
# must be #equal?
return 0
end

end

require 'test/unit'

Name = Resolv::DNS::Name

class TestDnsName < Test::Unit::TestCase

def test_what_I_think_are_odd_behaviours
# Why can't test against strings?
assert_equal(false, Name.create("example.CoM") == "example.com")
assert_equal(false, Name.create("example.CoM").eql?("example.com"))

# Why does making it absolute mean they aren't equal?
assert_equal(false, Name.create("example.CoM").eql?(Name.create("example.com.")))
assert_equal(false, Name.create("example.CoM") == Name.create("example.com."))
end

def test_CoMparisons

assert_equal(true, Name.create("example.CoM").eql?(Name.create("example.com")))
assert_equal(true, Name.create("example.CoM") == Name.create("example.com"))

assert_equal(true, Name.create("example.CoM").equal?("example.com."))
assert_equal(true, Name.create("example.CoM").equal?("example.com"))

assert_equal(true, Name.create("www.example.CoM") < "example.com")
assert_equal(true, Name.create("www.example.CoM") <= "example.com")
assert_equal(-1, Name.create("www.example.CoM") <=> "example.com")
assert_equal(false, Name.create("www.example.CoM") >= "example.com")
assert_equal(false, Name.create("www.example.CoM") > "example.com")

assert_equal(false, Name.create("example.CoM") < "example.com")
assert_equal(true, Name.create("example.CoM") <= "example.com")
assert_equal(0, Name.create("example.CoM") <=> "example.com")
assert_equal(true, Name.create("example.CoM") >= "example.com")
assert_equal(false, Name.create("example.CoM") > "example.com")

assert_equal(false, Name.create("CoM") < "example.com")
assert_equal(false, Name.create("CoM") <= "example.com")
assert_equal(+1, Name.create("CoM") <=> "example.com")
assert_equal(true, Name.create("CoM") >= "example.com")
assert_equal(true, Name.create("CoM") > "example.com")

assert_equal(nil, Name.create("bar.CoM") < "example.com")
assert_equal(nil, Name.create("bar.CoM") <= "example.com")
assert_equal(nil, Name.create("bar.CoM") <=> "example.com")
assert_equal(nil, Name.create("bar.CoM") >= "example.com")
assert_equal(nil, Name.create("bar.CoM") > "example.com")

assert_equal(nil, Name.create("net.") < "com")
assert_equal(nil, Name.create("net.") <= "com")
assert_equal(nil, Name.create("net.") <=> "com")
assert_equal(nil, Name.create("net.") >= "com")
assert_equal(nil, Name.create("net.") > "com")

end
end



15 Answers

Tanaka Akira

2/4/2005 4:25:00 AM

0

In article <20050131023128.GB13952@ensemble.local>,
Sam Roberts <sroberts@uniserve.com> writes:

> I don't understand why DNS::Name#== requires both to be absolute if one
> is.
>
> Is this really necessary/useful? It surprises me.

I think it's right behavior.

> Also, I have a set of comparison operations for Resolv::DNS::Name.
>
> I copied the style (and docs) from Module/hierarchy comparisons, because
> I think there is some similarity.
>
> Comments?

I'm not sure that they are used frequently enough to occupy comparison
operators.
--
Tanaka Akira


Sam Roberts

2/4/2005 5:30:00 AM

0

Quoteing akr@m17n.org, on Fri, Feb 04, 2005 at 01:25:20PM +0900:
> In article <20050131023128.GB13952@ensemble.local>,
> Sam Roberts <sroberts@uniserve.com> writes:
>
> > I don't understand why DNS::Name#== requires both to be absolute if one
> > is.
> >
> > Is this really necessary/useful? It surprises me.
>
> I think it's right behavior.

Why? When are they not the same?

They are equivalent in the DNS, Resolv::DNS#getaddress() would return
the same A record for both.

> > Also, I have a set of comparison operations for Resolv::DNS::Name.
>
> I'm not sure that they are used frequently enough to occupy comparison
> operators.

Name doesn't have any comparison operators, so they occupy empty space.

Do you have another idea on what Name < Name could mean?

I found it necessary to find whether a Name was a sub-domain of another.
How would you recommend to do this, if there is no method?

lhs.length > rhs.length && lhs.to_a[-rhs.length, rhs.length] == rhs.to_a

is the best I came up with, and its not the kind of thing I would want
to type regularly.

Cheers,
Sam



Tanaka Akira

2/4/2005 8:03:00 AM

0

In article <20050204052951.GA2531@ensemble.local>,
Sam Roberts <sroberts@uniserve.com> writes:

> Why? When are they not the same?

For example, if you have a machine named "museum", it is confusing with
"museum." domain.

> They are equivalent in the DNS, Resolv::DNS#getaddress() would return
> the same A record for both.

There are several top-domains which have A record: ac, museum, etc.
If your local domain have a machine named "ac", it is important to
distinguish a relative "ac" domain and the absolute "ac" domain.

> Name doesn't have any comparison operators, so they occupy empty space.
>
> Do you have another idea on what Name < Name could mean?

For example, lexical order.

> I found it necessary to find whether a Name was a sub-domain of another.
> How would you recommend to do this, if there is no method?
>
> lhs.length > rhs.length && lhs.to_a[-rhs.length, rhs.length] == rhs.to_a
>
> is the best I came up with, and its not the kind of thing I would want
> to type regularly.

Adding some method (not operator) is acceptable if it has a good name.
--
Tanaka Akira


Navindra Umanee

2/4/2005 8:08:00 AM

0

Sam Roberts <sroberts@uniserve.com> wrote:
> > > I don't understand why DNS::Name#== requires both to be absolute if one
> > > is.
> > >
> > > Is this really necessary/useful? It surprises me.
> >
> > I think it's right behavior.
>
> Why? When are they not the same?
>
> They are equivalent in the DNS, Resolv::DNS#getaddress() would return
> the same A record for both.

"example.com" is a relative domain name. Depending on your
resolver/nameserver's configuration, I believe it could resolve to
"example.com.DEFAULTDOMAIN." which wouldn't be the same as
"example.com."

Cheers,
Navin.


Sam Roberts

2/4/2005 3:08:00 PM

0

Quoteing akr@m17n.org, on Fri, Feb 04, 2005 at 05:02:45PM +0900:
> In article <20050204052951.GA2531@ensemble.local>,
> Sam Roberts <sroberts@uniserve.com> writes:
>
> > Why? When are they not the same?
>
> For example, if you have a machine named "museum", it is confusing with
> "museum." domain.
>
> > They are equivalent in the DNS, Resolv::DNS#getaddress() would return
> > the same A record for both.
>
> There are several top-domains which have A record: ac, museum, etc.
> If your local domain have a machine named "ac", it is important to
> distinguish a relative "ac" domain and the absolute "ac" domain.

You only sometimes distinguish between the two, sometimes you convert
them to each other:

>> Name = Resolv::DNS::Name
=> Resolv::DNS::Name
>> Name.create('museum') == Name.create('museum')
=> true
# But, wouldn't this depend on the location? On my net, museum would
# be museum.local.
>> Name.create('museum') == Name.create('museum.')
=> false
>> Name.create('museum').to_s == Name.create('museum.').to_s
=> true
# Isn't it important to distinguish?

n0=Resolv::DNS.new.getname("193.108.154.9")

n1=Resolv.getname("193.108.154.9")
=> "a193-108-154-9.deploy.akamaitechnologies.com"
# note it isn't absolute, though the DNS response was

n0 == n1
=> in `==': undefined method `absolute?' for "com":String (NoMethodError)
# I have to change my String to a Name to compare it? Irritating.

n1 = Resolv::DNS::Name.create(n0)
n0 == n1
=> false
# DNS returns absolute names
n2 = Resolv::DNS::Name.create(n0.to_s)
n0 == n2
=> false
n0.to_s == n2.to_s
=> true

Maybe you haven't seen how unuseful the behaviour of == is because you
use it inside resolv.rb, and all names you see are absolute?

Outside of resolv.rb (including input/output values of the common Resolv
class methods) names are non-absolute strings, and the DNS Name objects
can't be compared to them.

> > Name doesn't have any comparison operators, so they occupy empty space.
> >
> > Do you have another idea on what Name < Name could mean?
>
> For example, lexical order.
>
> > I found it necessary to find whether a Name was a sub-domain of another.
> > How would you recommend to do this, if there is no method?
> >
> > lhs.length > rhs.length && lhs.to_a[-rhs.length, rhs.length] == rhs.to_a
> >
> > is the best I came up with, and its not the kind of thing I would want
> > to type regularly.
>
> Adding some method (not operator) is acceptable if it has a good name.

The code I posted included the method #lt?, you could include it as is,
or change its name. For consistency with #eql?, you may want to make it
NOT convert its argument from String to Name, or you may want to change
both to allow comparison to String.

def lt?(name)
n = Name.create(name) # maybe remove?
length > n.length && to_a[-n.length, n.length] == n.to_a
end

def ==(other)
other = Name.create(other) # maybe add?
return @labels == other.to_a && @absolute == other.absolute?
end
alias eql? ==


Cheers,
Sam



Sam Roberts

2/5/2005 3:21:00 AM

0


The attached patch fixes this.

Note well that it does it in such a way that it is backwards compatible, but
also allows callers to see the strings as an array. I need this.

RFC1035 says "semantics of the text depends on the domain where it is found",
and the DNS-SD draft says mDNS TXT responses contain key/value pairs, where
each key/value pair is a <character-string> from the TXT rdata.

RFC1035:

3.3.14. TXT RDATA format

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/ TXT-DATA /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

where:

TXT-DATA One or more <character-string>s.

TXT RRs are used to hold descriptive text. The semantics of the text
depends on the domain where it is found.

Reproduce this with packet collected from wild:

require 'resolv.rb'
require 'pp'

data = "\000\000\000\000\000\003\000\003\000\000\000\000\025Sam Roberts\342\200\231s Music\005_daap\004_tcp\005local\000\000!\000\001\300\f\000\020\000\001\300\"\000\f\000\001\300\f\000!\000\001\000\000\000;\000\021\000\000\000\000\016i\010ensemble\300-\300\f\000\020\000\001\000\000\000;\000\224\ttxtvers=1\016Version=196608\023iTSh Version=131073\027Machine ID=9C5AC2725708\034Database ID=681233690CC1C418\"Machine Name=Sam Roberts\342\200\231s Music\016Password=false\300\"\000\f\000\001\000\000\034\037\000\002\300\f"

msg = Resolv::DNS::Message.decode(data)

pp msg

txtanswer = msg.answer[1]
txtrr = txtanswer[2]

pp txtrr.data
pp txtrr.datas


Changelog:

resolv.rb dies on TXT records with multiple strings

Patch:


Index: resolv.rb
===================================================================
RCS file: /src/ruby/lib/resolv.rb,v
retrieving revision 1.17.2.8
diff -u -r1.17.2.8 resolv.rb
--- resolv.rb 29 Jan 2005 05:22:35 -0000 1.17.2.8
+++ resolv.rb 5 Feb 2005 03:11:52 -0000
@@ -1266,6 +1266,14 @@
return d
end

+ def get_strings
+ strings = []
+ until @index == @limit
+ strings << get_string
+ end
+ strings
+ end
+
def get_name
return Name.new(self.get_labels)
end
@@ -1511,14 +1519,21 @@
def initialize(data)
@data = data
end
- attr_reader :data
+
+ def data
+ @data.join
+ end
+
+ def datas
+ @data
+ end

def encode_rdata(msg)
msg.put_string(@data)
end

def self.decode_rdata(msg)
- data = msg.get_string
+ data = msg.get_strings
return self.new(data)
end
end


Tanaka Akira

2/5/2005 4:28:00 AM

0

In article <20050204150754.GA2662@ensemble.local>,
Sam Roberts <sroberts@uniserve.com> writes:

> Maybe you haven't seen how unuseful the behaviour of == is because you
> use it inside resolv.rb, and all names you see are absolute?
>
> Outside of resolv.rb (including input/output values of the common Resolv
> class methods) names are non-absolute strings, and the DNS Name objects
> can't be compared to them.

It is caused by the absolute/relative difference is not cared outside
of a resolver. The unusefulness comes from you need supply omitted
absoluteness information for conversion from string to name, and
Resolv::DNS::Name#to_s omit the absoluteness information.

I believe Resolv::DNS::Name's absoluteness sensitive behavior prevents
a kind of bugs. So I don't want to introduce absoluteness insensitive
behavour such as your comparison.

Adding some conversion methods may be a solution. I don't have
concrete idea, though.

> The code I posted included the method #lt?, you could include it as is,
> or change its name. For consistency with #eql?, you may want to make it
> NOT convert its argument from String to Name, or you may want to change
> both to allow comparison to String.

I don't think lt? is a good name.
Because it may mean comparison in lexical order and other orders.
--
Tanaka Akira


Sam Roberts

2/5/2005 2:52:00 PM

0

Quoteing akr@m17n.org, on Sat, Feb 05, 2005 at 01:27:35PM +0900:
> > The code I posted included the method #lt?, you could include it as is,
> > or change its name. For consistency with #eql?, you may want to make it
> > NOT convert its argument from String to Name, or you may want to change
> > both to allow comparison to String.
>
> I don't think lt? is a good name.
> Because it may mean comparison in lexical order and other orders.

Perhaps #subdomain?

sam



Tanaka Akira

2/5/2005 6:36:00 PM

0

In article <20050205032049.GA13619@ensemble.local>,
Sam Roberts <sroberts@uniserve.com> writes:

> The attached patch fixes this.
>
> Note well that it does it in such a way that it is backwards compatible, but
> also allows callers to see the strings as an array. I need this.
>
> RFC1035 says "semantics of the text depends on the domain where it is found",
> and the DNS-SD draft says mDNS TXT responses contain key/value pairs, where
> each key/value pair is a <character-string> from the TXT rdata.

It is fixed based on your patch. Thank you.

Note that I added TXT#strings to retrieve all strings, instead of
TXT#datas.
--
Tanaka Akira


Tanaka Akira

2/5/2005 7:56:00 PM

0

In article <20050205145144.GA13678@ensemble.local>,
Sam Roberts <sroberts@uniserve.com> writes:

> Perhaps #subdomain?

The term "subdomain" is good.

But I feel two possibile meanings with A.subdomain?(B) :

* A is a subdomain of B
* B is a subdomain of A

Is it clear for native speakers?
--
Tanaka Akira