1 | # Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
|
---|
2 | #
|
---|
3 | # Permission to use, copy, modify, and distribute this software and its
|
---|
4 | # documentation for any purpose with or without fee is hereby granted,
|
---|
5 | # provided that the above copyright notice and this permission notice
|
---|
6 | # appear in all copies.
|
---|
7 | #
|
---|
8 | # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
|
---|
9 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
---|
10 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
|
---|
11 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
---|
12 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
---|
13 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
---|
14 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
---|
15 |
|
---|
16 | """DNS nodes. A node is a set of rdatasets."""
|
---|
17 |
|
---|
18 | import StringIO
|
---|
19 |
|
---|
20 | import dns.rdataset
|
---|
21 | import dns.rdatatype
|
---|
22 | import dns.renderer
|
---|
23 |
|
---|
24 | class Node(object):
|
---|
25 | """A DNS node.
|
---|
26 |
|
---|
27 | A node is a set of rdatasets
|
---|
28 |
|
---|
29 | @ivar rdatasets: the node's rdatasets
|
---|
30 | @type rdatasets: list of dns.rdataset.Rdataset objects"""
|
---|
31 |
|
---|
32 | __slots__ = ['rdatasets']
|
---|
33 |
|
---|
34 | def __init__(self):
|
---|
35 | """Initialize a DNS node.
|
---|
36 | """
|
---|
37 |
|
---|
38 | self.rdatasets = [];
|
---|
39 |
|
---|
40 | def to_text(self, name, **kw):
|
---|
41 | """Convert a node to text format.
|
---|
42 |
|
---|
43 | Each rdataset at the node is printed. Any keyword arguments
|
---|
44 | to this method are passed on to the rdataset's to_text() method.
|
---|
45 | @param name: the owner name of the rdatasets
|
---|
46 | @type name: dns.name.Name object
|
---|
47 | @rtype: string
|
---|
48 | """
|
---|
49 |
|
---|
50 | s = StringIO.StringIO()
|
---|
51 | for rds in self.rdatasets:
|
---|
52 | print >> s, rds.to_text(name, **kw)
|
---|
53 | return s.getvalue()[:-1]
|
---|
54 |
|
---|
55 | def __repr__(self):
|
---|
56 | return '<DNS node ' + str(id(self)) + '>'
|
---|
57 |
|
---|
58 | def __eq__(self, other):
|
---|
59 | """Two nodes are equal if they have the same rdatasets.
|
---|
60 |
|
---|
61 | @rtype: bool
|
---|
62 | """
|
---|
63 | #
|
---|
64 | # This is inefficient. Good thing we don't need to do it much.
|
---|
65 | #
|
---|
66 | for rd in self.rdatasets:
|
---|
67 | if rd not in other.rdatasets:
|
---|
68 | return False
|
---|
69 | for rd in other.rdatasets:
|
---|
70 | if rd not in self.rdatasets:
|
---|
71 | return False
|
---|
72 | return True
|
---|
73 |
|
---|
74 | def __ne__(self, other):
|
---|
75 | return not self.__eq__(other)
|
---|
76 |
|
---|
77 | def __len__(self):
|
---|
78 | return len(self.rdatasets)
|
---|
79 |
|
---|
80 | def __iter__(self):
|
---|
81 | return iter(self.rdatasets)
|
---|
82 |
|
---|
83 | def find_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE,
|
---|
84 | create=False):
|
---|
85 | """Find an rdataset matching the specified properties in the
|
---|
86 | current node.
|
---|
87 |
|
---|
88 | @param rdclass: The class of the rdataset
|
---|
89 | @type rdclass: int
|
---|
90 | @param rdtype: The type of the rdataset
|
---|
91 | @type rdtype: int
|
---|
92 | @param covers: The covered type. Usually this value is
|
---|
93 | dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
|
---|
94 | dns.rdatatype.RRSIG, then the covers value will be the rdata
|
---|
95 | type the SIG/RRSIG covers. The library treats the SIG and RRSIG
|
---|
96 | types as if they were a family of
|
---|
97 | types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much
|
---|
98 | easier to work with than if RRSIGs covering different rdata
|
---|
99 | types were aggregated into a single RRSIG rdataset.
|
---|
100 | @type covers: int
|
---|
101 | @param create: If True, create the rdataset if it is not found.
|
---|
102 | @type create: bool
|
---|
103 | @raises KeyError: An rdataset of the desired type and class does
|
---|
104 | not exist and I{create} is not True.
|
---|
105 | @rtype: dns.rdataset.Rdataset object
|
---|
106 | """
|
---|
107 |
|
---|
108 | for rds in self.rdatasets:
|
---|
109 | if rds.match(rdclass, rdtype, covers):
|
---|
110 | return rds
|
---|
111 | if not create:
|
---|
112 | raise KeyError
|
---|
113 | rds = dns.rdataset.Rdataset(rdclass, rdtype)
|
---|
114 | self.rdatasets.append(rds)
|
---|
115 | return rds
|
---|
116 |
|
---|
117 | def get_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE,
|
---|
118 | create=False):
|
---|
119 | """Get an rdataset matching the specified properties in the
|
---|
120 | current node.
|
---|
121 |
|
---|
122 | None is returned if an rdataset of the specified type and
|
---|
123 | class does not exist and I{create} is not True.
|
---|
124 |
|
---|
125 | @param rdclass: The class of the rdataset
|
---|
126 | @type rdclass: int
|
---|
127 | @param rdtype: The type of the rdataset
|
---|
128 | @type rdtype: int
|
---|
129 | @param covers: The covered type.
|
---|
130 | @type covers: int
|
---|
131 | @param create: If True, create the rdataset if it is not found.
|
---|
132 | @type create: bool
|
---|
133 | @rtype: dns.rdataset.Rdataset object or None
|
---|
134 | """
|
---|
135 |
|
---|
136 | try:
|
---|
137 | rds = self.find_rdataset(rdclass, rdtype, covers, create)
|
---|
138 | except KeyError:
|
---|
139 | rds = None
|
---|
140 | return rds
|
---|
141 |
|
---|
142 | def delete_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE):
|
---|
143 | """Delete the rdataset matching the specified properties in the
|
---|
144 | current node.
|
---|
145 |
|
---|
146 | If a matching rdataset does not exist, it is not an error.
|
---|
147 |
|
---|
148 | @param rdclass: The class of the rdataset
|
---|
149 | @type rdclass: int
|
---|
150 | @param rdtype: The type of the rdataset
|
---|
151 | @type rdtype: int
|
---|
152 | @param covers: The covered type.
|
---|
153 | @type covers: int
|
---|
154 | """
|
---|
155 |
|
---|
156 | rds = self.get_rdataset(rdclass, rdtype, covers)
|
---|
157 | if not rds is None:
|
---|
158 | self.rdatasets.remove(rds)
|
---|
159 |
|
---|
160 | def replace_rdataset(self, replacement):
|
---|
161 | """Replace an rdataset.
|
---|
162 |
|
---|
163 | It is not an error if there is no rdataset matching I{replacement}.
|
---|
164 |
|
---|
165 | Ownership of the I{replacement} object is transferred to the node;
|
---|
166 | in other words, this method does not store a copy of I{replacement}
|
---|
167 | at the node, it stores I{replacement} itself.
|
---|
168 | """
|
---|
169 |
|
---|
170 | self.delete_rdataset(replacement.rdclass, replacement.rdtype,
|
---|
171 | replacement.covers)
|
---|
172 | self.rdatasets.append(replacement)
|
---|