Initial import
[ldd.git] / ldd / dn.py
CommitLineData
769e7ed9 1# ldd - DNS implementation in Python
2# Copyright (C) 2006 Fredrik Tolf <fredrik@dolda2000.com>
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software
16# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
18class DNNotIn(Exception):
19 def __init__(self, a, b):
20 self.a = a
21 self.b = b
22
23 def __str__(self):
24 return str(self.a) + " not in " + str(self.b)
25
26class domainname:
27 "A class for abstract representations of domain names"
28
29 def __init__(self, parts, rooted):
30 self.parts = parts
31 self.rooted = rooted
32
33 def __repr__(self):
34 ret = ""
35 if len(self.parts) > 0:
36 for p in self.parts[:-1]:
37 ret = ret + p + '.'
38 ret = ret + self.parts[-1]
39 if self.rooted:
40 ret = ret + '.'
41 return ret
42
43 def __add__(self, y):
44 if self.rooted:
45 raise Exception("cannot append to a rooted domain name")
46 return(domainname(self.parts + y.parts, y.rooted))
47
48 def __getitem__(self, y):
49 return domainname([self.parts[y]], self.rooted and (y == -1 or y == len(self.parts) - 1))
50
51 def __getslice__(self, i, j):
52 return domainname(self.parts[i:j], self.rooted and j >= len(self.parts))
53
54 def __len__(self):
55 return len(self.parts)
56
57 def __eq__(self, y):
58 if type(y) == str:
59 y = fromstring(y)
60 if self.rooted != y.rooted:
61 return False
62 if len(self.parts) != len(y.parts):
63 return False
64 for i in range(len(self.parts)):
65 if self.parts[i].lower() != y.parts[i].lower():
66 return False
67 return True
68
69 def __ne__(self, y):
70 return not self.__eq__(y)
71
72 def __contains__(self, y):
73 if len(self) > len(y):
74 return False
75 if len(self) == 0:
76 return self.rooted == y.rooted
77 return y[-len(self):] == self
78
79 def __sub__(self, y):
80 if self not in y:
81 raise DNNotIn(self, y)
82 return self[:len(self) - len(y)]
83
84 def __hash__(self):
85 ret = 0
86 for part in self.parts:
87 ret = ret ^ hash(part)
88 if self.rooted:
89 ret = ret ^ -1
90 return ret
91
92 def canonwire(self):
93 ret = ""
94 for p in self.parts:
95 ret += chr(len(p))
96 ret += p.lower()
97 ret += chr(0)
98 return ret
99
100class DNError(Exception):
101 emptypart = 1
102 illegalchar = 2
103 def __init__(self, kind):
104 self.kind = kind
105 def __str__(self):
106 return {1: "empty part",
107 2: "illegal character"}[self.kind]
108
109def fromstring(name):
110 parts = []
111 if name == ".":
112 return domainname([], True)
113 if name == "":
114 return domainname([], False)
115 while name.find('.') >= 0:
116 cur = name.find('.')
117 if cur == 0:
118 raise DNError(DNError.emptypart)
119 part = name[:cur]
120 for c in part:
121 if ord(c) < 33:
122 raise DNError(DNError.illegalchar)
123 parts.append(part)
124 name = name[cur + 1:]
125 if len(name) > 0:
126 parts.append(name)
127 rooted = False
128 else:
129 rooted = True
130 return domainname(parts, rooted)
131