f



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

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



0
sroberts1 (233)
1/31/2005 2:31:48 AM
comp.lang.ruby 48886 articles. 0 followers. Post Follow

15 Replies
1092 Views

Similar Articles

[PageSpeed] 12

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


0
akr (147)
2/4/2005 4:25:20 AM
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



0
sroberts1 (233)
2/4/2005 5:30:13 AM
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


0
akr (147)
2/4/2005 8:02:45 AM
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.


0
navindra (162)
2/4/2005 8:07:37 AM
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



0
sroberts1 (233)
2/4/2005 3:08:13 PM
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


0
sroberts1 (233)
2/5/2005 3:21:09 AM
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


0
akr (147)
2/5/2005 4:27:35 AM
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



0
sroberts1 (233)
2/5/2005 2:52:08 PM
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


0
akr (147)
2/5/2005 6:36:04 PM
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


0
akr (147)
2/5/2005 7:55:36 PM
Quoteing akr@m17n.org, on Sun, Feb 06, 2005 at 04:55:36AM +0900:
> 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?

Native speakers are as confused by their language as everybody else.

#subdomainof?

would make it completely clear.

Sam



0
sroberts1 (233)
2/5/2005 9:40:28 PM
Quoteing akr@m17n.org, on Sun, Feb 06, 2005 at 03:36:04AM +0900:
> In article <20050205032049.GA13619@ensemble.local>,
> It is fixed based on your patch.  Thank you.
> 
> Note that I added TXT#strings to retrieve all strings, instead of
> TXT#datas.

I am happy with that.

Thanks,
Sam



0
sroberts1 (233)
2/5/2005 9:41:13 PM
On Sun, 6 Feb 2005 04:55:36 +0900, Tanaka Akira <akr@m17n.org> wrote:
> 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?

Might I suggest #include? instead? Thus, (using <> strings to
represent instances of the class under discussion):

  <.ca>.include?(<halostatue.ca>) # => true
  <halostatue.ca>.include?(<www.halostatue.ca>) # => true

But, I don't use this functionality at all, so you can safely ignore
my suggestion here.

-austin
-- 
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca


0
halostatue (1713)
2/5/2005 10:13:32 PM
In article <20050205214007.GA13730@ensemble.local>,
  Sam Roberts <sroberts@uniserve.com> writes:

> #subdomainof?
>
> would make it completely clear.

subdomain_of? and inspect is implemented.

I inserted a underscore to consistent with kind_of?.
-- 
Tanaka Akira


0
akr (147)
2/7/2005 3:26:36 PM
Quoteing akr@m17n.org, on Tue, Feb 08, 2005 at 12:26:36AM +0900:
> In article <20050205214007.GA13730@ensemble.local>,
>   Sam Roberts <sroberts@uniserve.com> writes:
> 
> > #subdomainof?
> >
> > would make it completely clear.
> 
> subdomain_of? and inspect is implemented.

Thank you.

Sam



0
sroberts1 (233)
2/7/2005 4:36:01 PM
Reply: