1#!/usr/bin/python3
2
3#
4# Tests of the packet assembler/disassembler routines.
5#
6# only tests the simple packers for now. next is to test the
7# classes: Hpacker/Hunpacker,
8# Qpacker/Unpacker, then Mpacker/Munpacker
9#
10# Start doing unpleasant tests with broken data, truncations, that
11# sort of thing.
12
13import sys ; sys.path.insert(0, '..')
14import DNS
15import socket
16import unittest
17
18TestCompleted = "TestCompleted" # exc.
19
20class Int16Packing(unittest.TestCase):
21    knownValues = ( ( 10, b'\x00\n'),
22                   ( 500, b'\x01\xf4' ),
23                   ( 5340, b'\x14\xdc' ),
24                   ( 51298, b'\xc8b'),
25                   ( 65535, b'\xff\xff'),
26                   )
27
28    def test16bitPacking(self):
29        """ pack16bit should give known output for known input """
30        for i,s in self.knownValues:
31            result = DNS.Lib.pack16bit(i)
32            self.assertEqual(s,result)
33
34    def test16bitUnpacking(self):
35        """ unpack16bit should give known output for known input """
36        for i,s in self.knownValues:
37            result = DNS.Lib.unpack16bit(s)
38            self.assertEqual(i,result)
39
40class Int32Packing(unittest.TestCase):
41    knownValues = ( ( 10, b'\x00\x00\x00\n'),
42                   ( 500, b'\x00\x00\x01\xf4' ),
43                   ( 5340, b'\x00\x00\x14\xdc' ),
44                   ( 51298, b'\x00\x00\xc8b'),
45                   ( 65535, b'\x00\x00\xff\xff'),
46                   ( 33265535, b'\x01\xfb\x97\x7f' ),
47                   ( 147483647, b'\x08\xcak\xff' ),
48                   ( 2147483647, b'\x7f\xff\xff\xff' ),
49                   )
50    def test32bitPacking(self):
51        """ pack32bit should give known output for known input """
52        for i,s in self.knownValues:
53            result = DNS.Lib.pack32bit(i)
54            self.assertEqual(s,result)
55
56    def test32bitUnpacking(self):
57        """ unpack32bit should give known output for known input """
58        for i,s in self.knownValues:
59            result = DNS.Lib.unpack32bit(s)
60            self.assertEqual(i,result)
61
62
63class IPaddrPacking(unittest.TestCase):
64    knownValues = (
65                    ('127.0.0.1', 2130706433 ),
66                    ('10.99.23.13', 174266125 ),
67                    ('192.35.59.45', 3223534381), # Not signed anymore - it's all long now.
68                    ('255.255.255.255', 4294967295) # No longer -1
69                    )
70
71    def testIPaddrPacking(self):
72        """ addr2bin should give known output for known input """
73        for i,s in self.knownValues:
74            result = DNS.Lib.addr2bin(i)
75            self.assertEqual(s,result)
76
77    def testIPaddrUnpacking(self):
78        """ bin2addr should give known output for known input """
79        for i,s in self.knownValues:
80            result = DNS.Lib.bin2addr(s)
81            self.assertEqual(i,result)
82
83class PackerClassPacking(unittest.TestCase):
84    knownPackValues = [
85        ( ['www.ekit.com'], b'\x03www\x04ekit\x03com\x00' ),
86        ( ['ns1.ekorp.com', 'ns2.ekorp.com', 'ns3.ekorp.com'],
87               b'\x03ns1\x05ekorp\x03com\x00\x03ns2\xc0\x04\x03ns3\xc0\x04'),
88        ( ['a.root-servers.net.', 'b.root-servers.net.',
89           'c.root-servers.net.', 'd.root-servers.net.',
90           'e.root-servers.net.', 'f.root-servers.net.'],
91               b'\x01a\x0croot-servers\x03net\x00\x01b\xc0\x02\x01c\xc0'+
92               b'\x02\x01d\xc0\x02\x01e\xc0\x02\x01f\xc0\x02' ),
93        ]
94    knownUnpackValues = [
95        ( ['www.ekit.com'], b'\x03www\x04ekit\x03com\x00' ),
96        ( ['ns1.ekorp.com', 'ns2.ekorp.com', 'ns3.ekorp.com'],
97               b'\x03ns1\x05ekorp\x03com\x00\x03ns2\xc0\x04\x03ns3\xc0\x04'),
98        ( ['a.root-servers.net', 'b.root-servers.net',
99           'c.root-servers.net', 'd.root-servers.net',
100           'e.root-servers.net', 'f.root-servers.net'],
101               b'\x01a\x0croot-servers\x03net\x00\x01b\xc0\x02\x01c\xc0'+
102               b'\x02\x01d\xc0\x02\x01e\xc0\x02\x01f\xc0\x02' ),
103        ]
104
105    def testPackNames(self):
106        from DNS.Lib import Packer
107        for namelist,result in self.knownPackValues:
108            p = Packer()
109            for n in namelist:
110                p.addname(n)
111            self.assertEqual(p.getbuf(),result)
112
113    def testUnpackNames(self):
114        from DNS.Lib import Unpacker
115        for namelist,result in self.knownUnpackValues:
116            u = Unpacker(result)
117            names = []
118            for i in range(len(namelist)):
119                n = u.getname()
120                names.append(n)
121            self.assertEqual(names, namelist)
122
123"""    def testUnpackerLimitCheck(self):
124       # FIXME: Don't understand what this test should do. If my guess is right,
125       # then the code is working ~OK.
126        from DNS.Lib import Unpacker
127        u=Unpacker(b'\x03ns1\x05ekorp\x03com\x00\x03ns2\xc0\x04\x03ns3\xc0\x04')
128        u.getname() ; u.getname() ; u.getname()
129        # 4th call should fail
130        self.assertRaises(IndexError, u.getname)"""
131
132class testUnpackingMangled(unittest.TestCase):
133    "addA(self, name, klass, ttl, address)"
134    packerCorrect = b'\x05www02\x04ekit\x03com\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x02'
135    def testWithoutRR(self):
136        u = DNS.Lib.RRunpacker(self.packerCorrect)
137        u.getAdata()
138    def testWithTwoRRs(self):
139        u = DNS.Lib.RRunpacker(self.packerCorrect)
140        u.getRRheader()
141        self.assertRaises(DNS.Lib.UnpackError, u.getRRheader)
142    def testWithNoGetData(self):
143        u = DNS.Lib.RRunpacker(self.packerCorrect)
144        u.getRRheader()
145        self.assertRaises(DNS.Lib.UnpackError, u.endRR)
146
147class PackerTestCase(unittest.TestCase):
148    " base class for tests of Packing code. Laziness on my part, I know. "
149    def setUp(self):
150        self.RRpacker = DNS.Lib.RRpacker
151        self.RRunpacker = DNS.Lib.RRunpacker
152
153    def testPacker(self):
154        p = self.RRpacker()
155        check = self.doPack(p)
156        if (p is not None) and (check is not TestCompleted):
157            return self.checkPackResult(p)
158
159    def checkPackResult(self, buf):
160        if not hasattr(self, 'packerExpectedResult'):
161            if self.__class__.__name__ != 'PackerTestCase':
162                print("P***", self, repr(buf.getbuf())) #cheat testcase
163        else:
164            return self.assertEqual(buf.getbuf(),
165                                self.packerExpectedResult)
166
167    def checkUnpackResult(self, rrbits, specbits):
168        if not hasattr(self, 'unpackerExpectedResult'):
169            if self.__class__.__name__ != 'PackerTestCase':
170                print("U***", self, repr((rrbits,specbits))) #cheat testcase
171        else:
172            return self.assertEqual((rrbits, specbits),
173                                self.unpackerExpectedResult)
174
175    def testUnpacker(self):
176        if self.doUnpack is not None:
177            if hasattr(self.__class__, 'doUnpack') \
178                    and hasattr(self, 'packerExpectedResult'):
179                u = self.RRunpacker(self.packerExpectedResult)
180                rrbits = u.getRRheader()[:4]
181                specbits = self.doUnpack(u)
182                try:
183                    u.endRR()
184                except DNS.Lib.UnpackError:
185                    self.assertEqual(0, 'Not at end of RR!')
186                return self.checkUnpackResult(rrbits, specbits)
187            else:
188                me = self.__class__.__name__
189                if me != 'PackerTestCase':
190                    self.assertEquals(self.__class__.__name__,
191                                                'Unpack NotImplemented')
192
193    def doPack(self, p):
194        " stub. don't test the base class "
195        return None
196
197    def doUnpack(self, p):
198        " stub. don't test the base class "
199        return None
200
201
202class testPackingOfCNAME(PackerTestCase):
203    "addCNAME(self, name, klass, ttl, cname)"
204    def doPack(self,p):
205        p.addCNAME('www.sub.domain', DNS.Class.IN, 3600, 'realhost.sub.domain')
206    def doUnpack(self, u):
207        return u.getCNAMEdata()
208
209    unpackerExpectedResult = (('www.sub.domain', DNS.Type.CNAME, DNS.Class.IN, 3600), 'realhost.sub.domain')
210    packerExpectedResult = \
211                b'\x03www\x03sub\x06domain\x00\x00\x05\x00\x01\x00'+ \
212                b'\x00\x0e\x10\x00\x0b\x08realhost\xc0\x04'
213
214class testPackingOfCNAME2(PackerTestCase):
215    "addCNAME(self, name, klass, ttl, cname)"
216    def doPack(self,p):
217        p.addCNAME('www.cust.com', DNS.Class.IN, 200, 'www023.big.isp.com')
218    def doUnpack(self, u):
219        return u.getCNAMEdata()
220    unpackerExpectedResult = (('www.cust.com', DNS.Type.CNAME, DNS.Class.IN, 200), 'www023.big.isp.com')
221    packerExpectedResult = \
222                b'\x03www\x04cust\x03com\x00\x00\x05\x00\x01\x00'+ \
223                b'\x00\x00\xc8\x00\x11\x06www023\x03big\x03isp\xc0\t'
224
225class testPackingOfCNAME3(PackerTestCase):
226    "addCNAME(self, name, klass, ttl, cname)"
227    def doPack(self,p):
228        p.addCNAME('www.fred.com', DNS.Class.IN, 86400, 'webhost.loa.com')
229    def doUnpack(self, u):
230        return u.getCNAMEdata()
231    unpackerExpectedResult = (('www.fred.com', DNS.Type.CNAME, DNS.Class.IN, 86400), 'webhost.loa.com')
232    packerExpectedResult = \
233                b'\x03www\x04fred\x03com\x00\x00\x05\x00\x01\x00\x01Q'+ \
234                b'\x80\x00\x0e\x07webhost\x03loa\xc0\t'
235
236class testPackingOfHINFO(PackerTestCase):
237    "addHINFO(self, name, klass, ttl, cpu, os)"
238    def doPack(self,p):
239        p.addHINFO('www.sub.domain.com', DNS.Class.IN, 3600, 'i686', 'linux')
240    def doUnpack(self, u):
241        return u.getHINFOdata()
242    unpackerExpectedResult = (('www.sub.domain.com', 13, 1, 3600), ('i686', 'linux'))
243    packerExpectedResult = \
244                b'\x03www\x03sub\x06domain\x03com\x00\x00\r\x00\x01'+ \
245                b'\x00\x00\x0e\x10\x00\x0b\x04i686\x05linux'
246
247class testPackingOfHINFO2(PackerTestCase):
248    "addHINFO(self, name, klass, ttl, cpu, os)"
249    def doPack(self,p):
250        p.addHINFO('core1.lax.foo.com', DNS.Class.IN, 3600, 'cisco', 'ios')
251    def doUnpack(self, u):
252        return u.getHINFOdata()
253    unpackerExpectedResult = (('core1.lax.foo.com', 13, 1, 3600), ('cisco', 'ios'))
254    packerExpectedResult = \
255                b'\x05core1\x03lax\x03foo\x03com\x00\x00\r\x00\x01'+ \
256                b'\x00\x00\x0e\x10\x00\n\x05cisco\x03ios'
257
258class testPackingOfMX(PackerTestCase):
259    "addMX(self, name, klass, ttl, preference, exchange)"
260    def doPack(self, p):
261        p.addMX('sub.domain.com', DNS.Class.IN, 86400, 10, 'mailhost1.isp.com')
262    def doUnpack(self, u):
263        return u.getMXdata()
264    packerExpectedResult = \
265                b'\x03sub\x06domain\x03com\x00\x00\x0f\x00\x01'+ \
266                b'\x00\x01Q\x80\x00\x12\x00\n\tmailhost1\x03isp\xc0\x0b'
267    unpackerExpectedResult = (('sub.domain.com', 15, 1, 86400), (10, 'mailhost1.isp.com'))
268
269class testPackingOfMX2(PackerTestCase):
270    "addMX(self, name, klass, ttl, preference, exchange)"
271    def doPack(self, p):
272        p.addMX('ekit-inc.com.', DNS.Class.IN, 86400, 10, 'mx1.ekorp.com')
273        p.addMX('ekit-inc.com.', DNS.Class.IN, 86400, 20, 'mx2.ekorp.com')
274        p.addMX('ekit-inc.com.', DNS.Class.IN, 86400, 30, 'mx3.ekorp.com')
275    def doUnpack(self, u):
276        res = [u.getMXdata(),]
277        dummy = u.getRRheader()[:4]
278        res += u.getMXdata()
279        dummy = u.getRRheader()[:4]
280        res += u.getMXdata()
281        return res
282    unpackerExpectedResult = (('ekit-inc.com', 15, 1, 86400), [(10, 'mx1.ekorp.com'), 20, 'mx2.ekorp.com', 30, 'mx3.ekorp.com'])
283    packerExpectedResult = \
284                b'\x08ekit-inc\x03com\x00\x00\x0f\x00\x01\x00\x01Q\x80\x00'+\
285                b'\x0e\x00\n\x03mx1\x05ekorp\xc0\t\x00\x00\x0f\x00\x01\x00'+\
286                b'\x01Q\x80\x00\x08\x00\x14\x03mx2\xc0\x1e\x00\x00\x0f\x00'+\
287                b'\x01\x00\x01Q\x80\x00\x08\x00\x1e\x03mx3\xc0\x1e'
288
289class testPackingOfNS(PackerTestCase):
290    "addNS(self, name, klass, ttl, nsdname)"
291    def doPack(self, p):
292        p.addNS('ekit-inc.com', DNS.Class.IN, 86400, 'ns1.ekorp.com')
293    def doUnpack(self, u):
294        return u.getNSdata()
295    unpackerExpectedResult = (('ekit-inc.com', 2, 1, 86400), 'ns1.ekorp.com')
296    packerExpectedResult = b'\x08ekit-inc\x03com\x00\x00\x02\x00\x01\x00\x01Q\x80\x00\x0c\x03ns1\x05ekorp\xc0\t'
297
298class testPackingOfPTR(PackerTestCase):
299    "addPTR(self, name, klass, ttl, ptrdname)"
300    def doPack(self, p):
301        p.addPTR('www.ekit-inc.com', DNS.Class.IN, 3600, 'www-real01.ekorp.com')
302    def doUnpack(self, u):
303        return u.getPTRdata()
304    unpackerExpectedResult = (('www.ekit-inc.com', 12, 1, 3600), 'www-real01.ekorp.com')
305    packerExpectedResult = b'\x03www\x08ekit-inc\x03com\x00\x00\x0c\x00\x01\x00\x00\x0e\x10\x00\x13\nwww-real01\x05ekorp\xc0\r'
306
307class testPackingOfSOA(PackerTestCase):
308    """addSOA(self, name, klass, ttl, mname,
309           rname, serial, refresh, retry, expire, minimum)"""
310    def doPack(self, p):
311        p.addSOA('ekit-inc.com', DNS.Class.IN, 3600, 'ns1.ekorp.com',
312                 'hostmaster.ekit-inc.com', 2002020301, 100, 200, 300, 400)
313    def doUnpack(self, u):
314        return u.getSOAdata()
315    unpackerExpectedResult = (('ekit-inc.com', 6, 1, 3600), ('ns1.ekorp.com', 'hostmaster', ('serial', 2002020301), ('refresh ', 100, '1 minutes'), ('retry', 200, '3 minutes'), ('expire', 300, '5 minutes'), ('minimum', 400, '6 minutes')))
316    packerExpectedResult = b'\x08ekit-inc\x03com\x00\x00\x06\x00\x01\x00\x00\x0e\x10\x00,\x03ns1\x05ekorp\xc0\t\nhostmaster\x00wTg\xcd\x00\x00\x00d\x00\x00\x00\xc8\x00\x00\x01,\x00\x00\x01\x90'
317
318
319class testPackingOfA(PackerTestCase):
320    "addA(self, name, klass, ttl, address)"
321    def doPack(self, p):
322        p.addA('www02.ekit.com', DNS.Class.IN, 86400, '192.168.10.2')
323    def doUnpack(self, u):
324        return u.getAdata()
325    unpackerExpectedResult = (('www02.ekit.com', 1, 1, 86400), '192.168.10.2')
326    packerExpectedResult = b'\x05www02\x04ekit\x03com\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x02'
327
328class testPackingOfA2(PackerTestCase):
329    "addA(self, name, ttl, address)"
330    def doPack(self, p):
331        p.addA('www.ekit.com', DNS.Class.IN, 86400, '10.98.1.0')
332    def doUnpack(self, u):
333        return u.getAdata()
334    unpackerExpectedResult = (('www.ekit.com', 1, 1, 86400), '10.98.1.0')
335    packerExpectedResult = b'\x03www\x04ekit\x03com\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\nb\x01\x00'
336
337class testPackingOfA3(PackerTestCase):
338    "addA(self, name, ttl, address)"
339    def doPack(self, p):
340        p.addA('www.zol.com', DNS.Class.IN, 86400, '192.168.10.4')
341        p.addA('www.zol.com', DNS.Class.IN, 86400, '192.168.10.3')
342        p.addA('www.zol.com', DNS.Class.IN, 86400, '192.168.10.2')
343        p.addA('www.zol.com', DNS.Class.IN, 86400, '192.168.10.1')
344    def doUnpack(self, u):
345        u1,d1,u2,d2,u3,d3,u4=u.getAdata(),u.getRRheader(),u.getAdata(),u.getRRheader(),u.getAdata(),u.getRRheader(),u.getAdata()
346        return u1,u2,u3,u4
347    unpackerExpectedResult = (('www.zol.com', 1, 1, 86400), ('192.168.10.4', '192.168.10.3', '192.168.10.2', '192.168.10.1'))
348    packerExpectedResult = b'\x03www\x03zol\x03com\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x04\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x03\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x02\x00\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xa8\n\x01'
349
350class testPackingOfTXT(PackerTestCase):
351    "addTXT(self, name, klass, ttl, list)"
352    def doPack(self, p):
353        p.addTXT('ekit-inc.com', DNS.Class.IN, 3600, 'this is a text record')
354    def doUnpack(self, u):
355        return u.getTXTdata()
356    packerExpectedResult = b'\x08ekit-inc\x03com\x00\x00\x10\x00\x01\x00\x00\x0e\x10\x00\x16\x15this is a text record'
357    unpackerExpectedResult = (('ekit-inc.com', 16, 1, 3600), [b'this is a text record'])
358
359# check what the maximum/minimum &c of TXT records are.
360class testPackingOfTXT2(PackerTestCase):
361    "addTXT(self, name, klass, ttl, list)"
362    def doPack(self, p):
363        f = lambda p=p:p.addTXT('ekit-inc.com', DNS.Class.IN, 3600, 'the quick brown fox jumped over the lazy brown dog\n'*20)
364        self.assertRaises(ValueError, f)
365        return TestCompleted
366    doUnpack = None
367
368class testPackingOfAAAAText(PackerTestCase):
369    "addAAAA(self, name, klass, ttl, address)"
370    def setUp(self):
371        self.RRpacker = DNS.Lib.RRpacker
372        self.RRunpacker = DNS.Lib.RRunpackerText
373
374    def doPack(self, p):
375        addAAAA(p, 'google.com', DNS.Class.IN, 4, '2607:f8b0:4005:802::1005')
376    def doUnpack(self, u):
377        r = u.getAAAAdata()
378        return r
379    packerExpectedResult = b'\x06google\x03com\x00\x00\x1c\x00\x01\x00\x00\x00\x04\x00\x10&\x07\xf8\xb0@\x05\x08\x02\x00\x00\x00\x00\x00\x00\x10\x05'
380    unpackerExpectedResult = (('google.com', DNS.Type.AAAA, DNS.Class.IN, 4), '2607:f8b0:4005:802::1005')
381
382class testPackingOfAAAABinary(PackerTestCase):
383    "addAAAA(self, name, klass, ttl, address)"
384    def setUp(self):
385        self.RRpacker = DNS.Lib.RRpacker
386        self.RRunpacker = DNS.Lib.RRunpackerBinary
387
388    def doPack(self, p):
389        addAAAA(p, 'google.com', DNS.Class.IN, 4, '2607:f8b0:4005:802::1005')
390    def doUnpack(self, u):
391        self.assertFalse(hasattr(u, "getAAAAdata"))
392        r = u.getbytes(16)
393        return r
394    packerExpectedResult = b'\x06google\x03com\x00\x00\x1c\x00\x01\x00\x00\x00\x04\x00\x10&\x07\xf8\xb0@\x05\x08\x02\x00\x00\x00\x00\x00\x00\x10\x05'
395    unpackerExpectedResult = (('google.com', DNS.Type.AAAA, DNS.Class.IN, 4), b'&\x07\xf8\xb0@\x05\x08\x02\x00\x00\x00\x00\x00\x00\x10\x05')
396
397class testPackingOfAAAAInteger(PackerTestCase):
398    "addAAAA(self, name, klass, ttl, address)"
399    def setUp(self):
400        self.RRpacker = DNS.Lib.RRpacker
401        self.RRunpacker = DNS.Lib.RRunpackerInteger
402
403    def doPack(self, p):
404        addAAAA(p, 'google.com', DNS.Class.IN, 4, '2607:f8b0:4005:802::1005')
405    def doUnpack(self, u):
406        r = u.getAAAAdata()
407        return r
408    packerExpectedResult = b'\x06google\x03com\x00\x00\x1c\x00\x01\x00\x00\x00\x04\x00\x10&\x07\xf8\xb0@\x05\x08\x02\x00\x00\x00\x00\x00\x00\x10\x05'
409    unpackerExpectedResult = (('google.com', DNS.Type.AAAA, DNS.Class.IN, 4), 50552053919387978162022445795852161029)
410
411def addAAAA(p, name, klass, ttl, address):
412    """Add AAAA record to a packer.
413    """
414    addr_buf = socket.inet_pton(socket.AF_INET6, address)
415    p.addRRheader(name, DNS.Type.AAAA, klass, ttl)
416    p.buf = p.buf + addr_buf
417    p.endRR()
418    return p
419
420#class testPackingOfQuestion(PackerTestCase):
421#    "addQuestion(self, qname, qtype, qclass)"
422#    def doPack(self, p):
423#        self.assertEquals(0,"NotImplemented")
424
425def test_suite():
426    from unittest import TestLoader
427    return TestLoader().loadTestsFromName(__name__)
428
429if __name__ == "__main__":
430    unittest.main()
431