]>
Commit | Line | Data |
---|---|---|
eb353acc RG |
1 | #!/usr/bin/env python2 |
2 | ||
3 | import errno | |
4 | import shutil | |
5 | import os | |
6 | import socket | |
7 | import struct | |
8 | import subprocess | |
9 | import sys | |
10 | import time | |
11 | import unittest | |
12 | import dns | |
13 | import dns.message | |
14 | ||
15 | class IXFRDistTest(unittest.TestCase): | |
16 | ||
17 | _ixfrDistStartupDelay = 2.0 | |
18 | _ixfrDistPort = 5342 | |
19 | ||
20 | _config_template = """ | |
21 | listen: | |
22 | - '127.0.0.1:%d' | |
23 | acl: | |
24 | - '127.0.0.0/8' | |
25 | axfr-timeout: 20 | |
26 | keep: 20 | |
27 | tcp-in-threads: 10 | |
28 | work-dir: 'ixfrdist.dir' | |
29 | failed-soa-retry: 3 | |
30 | """ | |
31 | _config_domains = None | |
32 | _config_params = ['_ixfrDistPort'] | |
33 | ||
34 | @classmethod | |
35 | def startIXFRDist(cls): | |
36 | print("Launching ixfrdist..") | |
37 | conffile = 'ixfrdist.yml' | |
38 | params = tuple([getattr(cls, param) for param in cls._config_params]) | |
39 | print(params) | |
40 | with open(conffile, 'w') as conf: | |
41 | conf.write("# Autogenerated by ixfrdisttests.py\n") | |
42 | conf.write(cls._config_template % params) | |
43 | ||
44 | if cls._config_domains is not None: | |
45 | conf.write("domains:\n") | |
46 | ||
47 | for domain, master in cls._config_domains.items(): | |
48 | conf.write(" - domain: %s\n" % (domain)) | |
49 | conf.write(" master: %s\n" % (master)) | |
50 | ||
51 | ixfrdistcmd = [os.environ['IXFRDISTBIN'], '--config', conffile, '--debug'] | |
52 | ||
53 | logFile = 'ixfrdist.log' | |
54 | with open(logFile, 'w') as fdLog: | |
55 | cls._ixfrdist = subprocess.Popen(ixfrdistcmd, close_fds=True, | |
56 | stdout=fdLog, stderr=fdLog) | |
57 | ||
58 | if 'IXFRDIST_FAST_TESTS' in os.environ: | |
59 | delay = 0.5 | |
60 | else: | |
61 | delay = cls._ixfrDistStartupDelay | |
62 | ||
63 | time.sleep(delay) | |
64 | ||
65 | if cls._ixfrdist.poll() is not None: | |
66 | cls._ixfrdist.kill() | |
67 | sys.exit(cls._ixfrdist.returncode) | |
68 | ||
69 | @classmethod | |
70 | def setUpSockets(cls): | |
71 | print("Setting up UDP socket..") | |
72 | cls._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
73 | cls._sock.settimeout(2.0) | |
74 | cls._sock.connect(("127.0.0.1", cls._ixfrDistPort)) | |
75 | ||
76 | @classmethod | |
77 | def setUpClass(cls): | |
78 | cls.startIXFRDist() | |
79 | cls.setUpSockets() | |
80 | ||
81 | print("Launching tests..") | |
82 | ||
83 | @classmethod | |
84 | def tearDownClass(cls): | |
85 | cls.tearDownIXFRDist() | |
86 | ||
87 | @classmethod | |
88 | def tearDownIXFRDist(cls): | |
89 | if 'IXFRDIST_FAST_TESTS' in os.environ: | |
90 | delay = 0.1 | |
91 | else: | |
92 | delay = 1.0 | |
93 | ||
94 | try: | |
95 | if cls._ixfrdist: | |
96 | cls._ixfrdist.terminate() | |
97 | if cls._ixfrdist.poll() is None: | |
98 | time.sleep(delay) | |
99 | if cls._ixfrdist.poll() is None: | |
100 | cls._ixfrdist.kill() | |
101 | cls._ixfrdist.wait() | |
102 | except OSError as e: | |
103 | # There is a race-condition with the poll() and | |
104 | # kill() statements, when the process is dead on the | |
105 | # kill(), this is fine | |
106 | if e.errno != errno.ESRCH: | |
107 | raise | |
108 | ||
109 | @classmethod | |
110 | def sendUDPQuery(cls, query, timeout=2.0, decode=True, fwparams=dict()): | |
111 | if timeout: | |
112 | cls._sock.settimeout(timeout) | |
113 | ||
114 | try: | |
115 | cls._sock.send(query.to_wire()) | |
116 | data = cls._sock.recv(4096) | |
117 | except socket.timeout: | |
118 | data = None | |
119 | finally: | |
120 | if timeout: | |
121 | cls._sock.settimeout(None) | |
122 | ||
123 | message = None | |
124 | if data: | |
125 | if not decode: | |
126 | return data | |
127 | message = dns.message.from_wire(data, **fwparams) | |
128 | return message | |
129 | ||
130 | # FIXME: sendTCPQuery and sendTCPQueryMultiResponse, when they are done reading | |
131 | # should wait for a short while on the socket to see if more data is coming | |
132 | # and error if it does! | |
133 | @classmethod | |
134 | def sendTCPQuery(cls, query, timeout=2.0): | |
135 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
136 | if timeout: | |
137 | sock.settimeout(timeout) | |
138 | ||
139 | sock.connect(("127.0.0.1", cls._ixfrDistPort)) | |
140 | ||
141 | try: | |
142 | wire = query.to_wire() | |
143 | sock.send(struct.pack("!H", len(wire))) | |
144 | sock.send(wire) | |
145 | data = sock.recv(2) | |
146 | if data: | |
147 | (datalen,) = struct.unpack("!H", data) | |
148 | data = sock.recv(datalen) | |
149 | except socket.timeout as e: | |
150 | print("Timeout: %s" % (str(e))) | |
151 | data = None | |
152 | except socket.error as e: | |
153 | print("Network error: %s" % (str(e))) | |
154 | data = None | |
155 | finally: | |
156 | sock.close() | |
157 | ||
158 | message = None | |
159 | if data: | |
160 | message = dns.message.from_wire(data) | |
161 | return message | |
162 | ||
163 | @classmethod | |
164 | def sendTCPQueryMultiResponse(cls, query, timeout=2.0, count=1): | |
165 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
166 | if timeout: | |
167 | sock.settimeout(timeout) | |
168 | ||
169 | sock.connect(("127.0.0.1", cls._ixfrDistPort)) | |
170 | ||
171 | try: | |
172 | wire = query.to_wire() | |
173 | sock.send(struct.pack("!H", len(wire))) | |
174 | sock.send(wire) | |
175 | except socket.timeout as e: | |
176 | raise Exception("Timeout: %s" % (str(e))) | |
177 | except socket.error as e: | |
178 | raise Exception("Network error: %s" % (str(e))) | |
179 | ||
180 | messages = [] | |
181 | for i in range(count): | |
182 | try: | |
183 | data = sock.recv(2) | |
184 | if data: | |
185 | (datalen,) = struct.unpack("!H", data) | |
186 | data = sock.recv(datalen) | |
187 | messages.append(dns.message.from_wire(data)) | |
188 | else: | |
189 | break | |
190 | except socket.timeout as e: | |
191 | raise Exception("Timeout: %s" % (str(e))) | |
192 | except socket.error as e: | |
193 | raise Exception("Network error: %s" % (str(e))) | |
194 | ||
195 | return messages | |
196 | ||
197 | def setUp(self): | |
198 | # This function is called before every tests | |
199 | return | |
200 |