Status: | Merged |
---|---|
Merged at revision: | 6434 |
Proposed branch: | lp:~ptman/hipl/cleanup |
Merge into: | lp:hipl |
Diff against target: |
1205 lines (+547/-282) 6 files modified
debian/hipl-dnsproxy.init (+1/-1) tools/hipdnsproxy/dnsproxy.py (+319/-264) tools/hipdnsproxy/dnsproxy_test.py (+182/-0) tools/hipdnsproxy/hipdnsproxy.in (+11/-5) tools/hipdnsproxy/resolvconf.py (+23/-7) tools/hipdnsproxy/util.py (+11/-5) |
To merge this branch: | bzr merge lp:~ptman/hipl/cleanup |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Miika Komu | Approve | ||
Review via email: mp+190185@code.launchpad.net |
Commit message
Description of the change
This branch consists of various polishing and cleanup commits.
- Fix (retry and backoff) for a race condition
- Fix logging to syslog
- Fix upstream DNS server change when moving from one network to another
- Source format improvement as reported by pylint and pep8
- Split huge mainloop into smaller, testable pieces and added tests
To post a comment you must log in.
lp:~ptman/hipl/cleanup
updated
- 6441. By Paul Tötterman
-
Fix several crashes when hipconf daemon cannot reach hipd
lp:~ptman/hipl/cleanup
updated
- 6442. By Paul Tötterman
-
Merged trunk
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'debian/hipl-dnsproxy.init' |
2 | --- debian/hipl-dnsproxy.init 2013-03-06 14:07:36 +0000 |
3 | +++ debian/hipl-dnsproxy.init 2013-10-11 15:24:31 +0000 |
4 | @@ -9,7 +9,7 @@ |
5 | # Short-Description: HIP DNS proxy |
6 | ### END INIT INFO |
7 | |
8 | -DNSPROXY_OPTS='-k' |
9 | +DNSPROXY_OPTS='-kS' |
10 | PID_FILE=/var/run/hipdnsproxy.pid |
11 | EXEC=/usr/sbin/hipdnsproxy |
12 | |
13 | |
14 | === modified file 'tools/hipdnsproxy/dnsproxy.py' |
15 | --- tools/hipdnsproxy/dnsproxy.py 2013-07-11 15:08:01 +0000 |
16 | +++ tools/hipdnsproxy/dnsproxy.py 2013-10-11 15:24:31 +0000 |
17 | @@ -67,6 +67,7 @@ |
18 | import logging |
19 | import logging.handlers |
20 | import os |
21 | +import pprint |
22 | import re |
23 | import select |
24 | import signal |
25 | @@ -79,10 +80,10 @@ |
26 | |
27 | # prepending (instead of appending) to make sure hosts.py does not |
28 | # collide with the system default |
29 | +import DNS |
30 | import hosts |
31 | import resolvconf |
32 | import util |
33 | -from DNS import Serialize, DeSerialize, Type |
34 | |
35 | |
36 | DEFAULT_HOSTS = '/etc/hosts' |
37 | @@ -94,6 +95,7 @@ |
38 | sys.stderr.write('Usage: %s\n' % os.path.split(sys.argv[0])[1]) |
39 | if msg: |
40 | sys.stderr.write('Error: %r\n' % msg) |
41 | + |
42 | sys.exit(1) |
43 | |
44 | |
45 | @@ -103,44 +105,58 @@ |
46 | def add_hit_ip_map(hit, addr): |
47 | """Add IP for HIT.""" |
48 | logging.info('Associating HIT %s with IP %s', hit, addr) |
49 | - subprocess.check_call(['hipconf', 'daemon', 'add', 'map', hit, addr], |
50 | - stdout=open(os.devnull, 'w'), |
51 | - stderr=subprocess.STDOUT) |
52 | + try: |
53 | + subprocess.check_call(['hipconf', 'daemon', 'add', 'map', hit, addr], |
54 | + stdout=open(os.devnull, 'w'), |
55 | + stderr=subprocess.STDOUT) |
56 | + except subprocess.CalledProcessError: |
57 | + logging.error('Got error from `hipconf daemon ...`. Is hipd up?') |
58 | |
59 | |
60 | def hit_to_lsi(hit): |
61 | """Return LSI for HIT if found.""" |
62 | - output = subprocess.Popen(['hipconf', 'daemon', 'hit-to-lsi', hit], |
63 | - stdout=subprocess.PIPE, |
64 | - stderr=subprocess.STDOUT).stdout |
65 | - |
66 | - for line in output: |
67 | - match = LSI_RE.search(line) |
68 | - if match: |
69 | - return match.group('lsi') |
70 | + proc = subprocess.Popen(['hipconf', 'daemon', 'hit-to-lsi', hit], |
71 | + stdout=subprocess.PIPE, |
72 | + stderr=subprocess.STDOUT) |
73 | + |
74 | + if proc.returncode == 254: |
75 | + logging.error('Cannot contact hipd. Is it running?') |
76 | + return |
77 | + |
78 | + output = proc.stdout |
79 | + |
80 | + try: |
81 | + for line in output: |
82 | + match = LSI_RE.search(line) |
83 | + if match: |
84 | + return match.group('lsi') |
85 | + except IOError: |
86 | + logging.error('Cannot read from `hipconf daemon ...`. Is hipd up?') |
87 | |
88 | |
89 | def lsi_to_hit(lsi): |
90 | """Return HIT for LSI if found.""" |
91 | - output = subprocess.Popen(['hipconf', 'daemon', 'lsi-to-hit', lsi], |
92 | - stdout=subprocess.PIPE, |
93 | - stderr=subprocess.STDOUT).stdout |
94 | - |
95 | - for line in output: |
96 | - match = hosts.HIT_RE.search(line) |
97 | - if match: |
98 | - return match.group('hit') |
99 | + proc = subprocess.Popen(['hipconf', 'daemon', 'lsi-to-hit', lsi], |
100 | + stdout=subprocess.PIPE, |
101 | + stderr=subprocess.STDOUT) |
102 | + |
103 | + if proc.returncode == 254: |
104 | + logging.error('Cannot contact hipd. Is it running?') |
105 | + return |
106 | + |
107 | + output = proc.stdout |
108 | + |
109 | + try: |
110 | + for line in output: |
111 | + match = hosts.HIT_RE.search(line) |
112 | + if match: |
113 | + return match.group('hit') |
114 | + except IOError: |
115 | + logging.error('Cannot read from `hipconf daemon ...`. Is hipd up?') |
116 | |
117 | |
118 | def is_reverse_hit_query(name): |
119 | - """Check if the query is a reverse query to a HIT. |
120 | - |
121 | - >>> is_reverse_hit_query('::1') |
122 | - False |
123 | - >>> is_reverse_hit_query('8.e.b.8.b.3.c.9.1.a.0.c.e.e.2.c.c.e.d.0.9.c.' |
124 | - ... '9.a.e.1.0.0.1.0.0.2.hit-to-ip.infrahip.net') |
125 | - True |
126 | - """ |
127 | + """Check if the query is a reverse query to a HIT.""" |
128 | if (name.endswith('.1.0.0.1.0.0.2.hit-to-ip.infrahip.net') and |
129 | len(name) == 86): |
130 | return True |
131 | @@ -156,6 +172,7 @@ |
132 | pidfile=None, prefix=None, server_ip=None, server_port=None): |
133 | self.bind_ip = bind_ip |
134 | self.bind_port = bind_port |
135 | + self.connected = False |
136 | self.disable_lsi = disable_lsi |
137 | self.dns_timeout = dns_timeout |
138 | self.fork = fork |
139 | @@ -171,10 +188,13 @@ |
140 | if self.hostsnames is None: |
141 | self.hostsnames = [] |
142 | |
143 | + self.clisock = None |
144 | + self.servsock = None |
145 | self.app_timeout = 1 |
146 | self.hosts_ttl = 122 |
147 | self.sent_queue = [] |
148 | self.hosts = None |
149 | + self.query_id = 1 |
150 | # Keyed by ('server_ip',server_port,query_id) tuple |
151 | self.sent_queue_d = {} |
152 | # required for ifconfig and hipconf in Fedora |
153 | @@ -217,20 +237,26 @@ |
154 | env = os.environ |
155 | if self.server_ip is None: |
156 | self.server_ip = env.get('SERVER', None) |
157 | + |
158 | if self.server_port is None: |
159 | server_port = env.get('SERVERPORT', None) |
160 | if server_port is not None: |
161 | self.server_port = int(server_port) |
162 | + |
163 | if self.server_port is None: |
164 | self.server_port = 53 |
165 | + |
166 | if self.bind_ip is None: |
167 | self.bind_ip = env.get('IP', None) |
168 | + |
169 | if self.bind_ip is None: |
170 | self.bind_ip = '127.0.0.53' |
171 | + |
172 | if self.bind_port is None: |
173 | bind_port = env.get('PORT', None) |
174 | if bind_port is not None: |
175 | self.bind_port = int(bind_port) |
176 | + |
177 | if self.bind_port is None: |
178 | self.bind_port = 53 |
179 | |
180 | @@ -238,7 +264,6 @@ |
181 | """Recheck all hosts files.""" |
182 | for hostsdb in self.hosts: |
183 | hostsdb.recheck() |
184 | - return |
185 | |
186 | def getaddr(self, ahn): |
187 | """Get a hostname matching address.""" |
188 | @@ -273,7 +298,6 @@ |
189 | if result: |
190 | return result |
191 | |
192 | - |
193 | def killold(self): |
194 | """Kill process with PID from pidfile.""" |
195 | try: |
196 | @@ -284,6 +308,7 @@ |
197 | else: |
198 | logging.error('Error opening pid file: %s', ioe) |
199 | sys.exit(1) |
200 | + |
201 | try: |
202 | os.kill(int(ifile.readline().rstrip()), signal.SIGTERM) |
203 | except OSError, ose: |
204 | @@ -293,6 +318,7 @@ |
205 | else: |
206 | logging.error('Error terminating old process: %s', ose) |
207 | sys.exit(1) |
208 | + |
209 | time.sleep(3) |
210 | ifile.close() |
211 | |
212 | @@ -306,6 +332,7 @@ |
213 | else: |
214 | logging.error('Error opening pid file: %s', ioe) |
215 | sys.exit(1) |
216 | + |
217 | ifile.readline() |
218 | global MYID |
219 | MYID = ifile.readline().rstrip() |
220 | @@ -319,6 +346,7 @@ |
221 | except IOError, ioe: |
222 | logging.error('Error opening pid file for writing: %s', ioe) |
223 | sys.exit(1) |
224 | + |
225 | global MYID |
226 | MYID = '%d-%d' % (time.time(), os.getpid()) |
227 | ofile.write('%d\n' % (os.getpid(),)) |
228 | @@ -338,19 +366,22 @@ |
229 | proc = subprocess.Popen(['ifconfig', 'dummy0'], |
230 | stdout=subprocess.PIPE, |
231 | stderr=subprocess.STDOUT).stdout |
232 | - result = proc.readline() |
233 | - while result: |
234 | - start = result.find('2001:1') |
235 | - end = result.find('/28') |
236 | + line = proc.readline() |
237 | + while line: |
238 | + start = line.find('2001:1') |
239 | + end = line.find('/28') |
240 | if start != -1 and end != -1: |
241 | - hit = result[start:end] |
242 | + hit = line[start:end] |
243 | if not self.getaddr(hit): |
244 | localhit.append(hit) |
245 | - result = proc.readline() |
246 | + |
247 | + line = proc.readline() |
248 | + |
249 | proc.close() |
250 | ofile = open(self.hiphosts, 'a') |
251 | for i in range(len(localhit)): |
252 | ofile.write('%s\tlocalhit%s\n' % (localhit[i], i + 1)) |
253 | + |
254 | ofile.close() |
255 | |
256 | def hip_cache_lookup(self, packet): |
257 | @@ -364,12 +395,13 @@ |
258 | |
259 | # convert 1.2....1.0.0.1.0.0.2.ip6.arpa to a HIT and |
260 | # map host name to address from cache |
261 | - if qtype == Type.PTR: |
262 | + if qtype == DNS.Type.PTR: |
263 | lr_ptr = None |
264 | addr_str = hosts.ptr_to_addr(qname) |
265 | if (not self.disable_lsi and addr_str is not None and |
266 | hosts.valid_lsi(addr_str)): |
267 | addr_str = lsi_to_hit(addr_str) |
268 | + |
269 | lr_ptr = self.getaddr(addr_str) |
270 | lr_aaaa_hit = None |
271 | else: |
272 | @@ -382,26 +414,29 @@ |
273 | packet['questions'][0][0].startswith(self.prefix))): |
274 | if lr_a is not None: |
275 | add_hit_ip_map(lr_aaaa_hit[0], lr_a[0]) |
276 | + |
277 | if lr_aaaa is not None: |
278 | add_hit_ip_map(lr_aaaa_hit[0], lr_aaaa[0]) |
279 | - if qtype == Type.AAAA: |
280 | + |
281 | + if qtype == DNS.Type.AAAA: |
282 | result = lr_aaaa_hit |
283 | - elif qtype == Type.A and not self.disable_lsi: |
284 | + elif qtype == DNS.Type.A and not self.disable_lsi: |
285 | lsi = hit_to_lsi(lr_aaaa_hit[0]) |
286 | if lsi is not None: |
287 | result = (lsi, lr_aaaa_hit[1]) |
288 | + |
289 | elif self.prefix and packet['questions'][0][0].startswith(self.prefix): |
290 | result = None |
291 | - elif qtype == Type.AAAA: |
292 | + elif qtype == DNS.Type.AAAA: |
293 | result = lr_aaaa |
294 | - elif qtype == Type.A: |
295 | + elif qtype == DNS.Type.A: |
296 | result = lr_a |
297 | - elif qtype == Type.PTR and lr_ptr is not None: |
298 | + elif qtype == DNS.Type.PTR and lr_ptr is not None: |
299 | result = (lr_ptr, self.hosts_ttl) |
300 | |
301 | if result is not None: |
302 | packet['answers'].append([packet['questions'][0][0], qtype, 1, |
303 | - result[1], result[0]]) |
304 | + result[1], result[0]]) |
305 | packet['ancount'] = len(packet['answers']) |
306 | packet['qr'] = 1 |
307 | return True |
308 | @@ -415,7 +450,7 @@ |
309 | |
310 | dns_hit_found = False |
311 | for answer in packet['answers']: |
312 | - if answer[1] == Type.HIP: |
313 | + if answer[1] == DNS.Type.HIP: |
314 | dns_hit_found = True |
315 | break |
316 | |
317 | @@ -426,20 +461,20 @@ |
318 | lsi_ans = [] |
319 | |
320 | for answer in packet['answers']: |
321 | - if answer[1] != Type.HIP: |
322 | + if answer[1] != DNS.Type.HIP: |
323 | continue |
324 | |
325 | hit = socket.inet_ntop(socket.AF_INET6, answer[7]) |
326 | - hit_ans.append([qname, Type.AAAA, 1, answer[3], hit]) |
327 | + hit_ans.append([qname, DNS.Type.AAAA, 1, answer[3], hit]) |
328 | |
329 | - if qtype == Type.A and not self.disable_lsi: |
330 | + if qtype == DNS.Type.A and not self.disable_lsi: |
331 | lsi = hit_to_lsi(hit) |
332 | if lsi is not None: |
333 | lsi_ans.append([qname, 1, 1, self.hosts_ttl, lsi]) |
334 | |
335 | self.cache_name(qname, hit, answer[3]) |
336 | |
337 | - if qtype == Type.AAAA and hit_found: |
338 | + if qtype == DNS.Type.AAAA and hit_found: |
339 | packet['answers'] = hit_ans |
340 | elif lsi is not None: |
341 | packet['answers'] = lsi_ans |
342 | @@ -448,10 +483,211 @@ |
343 | |
344 | packet['ancount'] = len(packet['answers']) |
345 | |
346 | + def handle_query(self, packet, sender): |
347 | + """Handle DNS query from downstream client.""" |
348 | + qtype = packet['questions'][0][1] |
349 | + |
350 | + sent_answer = False |
351 | + |
352 | + if qtype in (DNS.Type.A, DNS.Type.AAAA, DNS.Type.PTR): |
353 | + if self.hip_cache_lookup(packet): |
354 | + try: |
355 | + outbuf = DNS.Serialize(packet).get_packet() |
356 | + self.servsock.sendto(outbuf, sender) |
357 | + sent_answer = True |
358 | + except socket.error: |
359 | + logging.exception('Exception:') |
360 | + |
361 | + elif (self.prefix and |
362 | + packet['questions'][0][0].startswith( |
363 | + self.prefix)): |
364 | + # Query with HIP prefix for unsupported RR type. |
365 | + # Send empty response. |
366 | + packet['qr'] = 1 |
367 | + try: |
368 | + outbuf = DNS.Serialize(packet).get_packet() |
369 | + self.servsock.sendto(outbuf, sender) |
370 | + sent_answer = True |
371 | + except socket.error: |
372 | + logging.exception('Exception:') |
373 | + |
374 | + if self.connected and not sent_answer: |
375 | + logging.info('Query type %d for %s from %s', |
376 | + qtype, packet['questions'][0][0], |
377 | + (self.server_ip, self.server_port)) |
378 | + |
379 | + query = (packet, sender[0], sender[1], qtype) |
380 | + # FIXME: Should randomize for security |
381 | + self.query_id = (self.query_id % 65535) + 1 |
382 | + pckt = copy.copy(packet) |
383 | + pckt['id'] = self.query_id |
384 | + if ((qtype == DNS.Type.AAAA or |
385 | + (qtype == DNS.Type.A and |
386 | + not self.disable_lsi)) and |
387 | + not is_reverse_hit_query( |
388 | + packet['questions'][0][0])): |
389 | + |
390 | + if not self.prefix: |
391 | + pckt['questions'][0][1] = DNS.Type.HIP |
392 | + |
393 | + if (self.prefix and |
394 | + pckt['questions'][0][0].startswith( |
395 | + self.prefix)): |
396 | + pckt['questions'][0][0] = pckt[ |
397 | + 'questions'][0][0][len(self.prefix):] |
398 | + pckt['questions'][0][1] = DNS.Type.HIP |
399 | + |
400 | + if qtype == DNS.Type.PTR and not self.disable_lsi: |
401 | + qname = packet['questions'][0][0] |
402 | + addr_str = hosts.ptr_to_addr(qname) |
403 | + if (addr_str is not None and |
404 | + hosts.valid_lsi(addr_str)): |
405 | + query = (packet, sender[0], sender[1], |
406 | + qname) |
407 | + hit_str = lsi_to_hit(addr_str) |
408 | + if hit_str is not None: |
409 | + pckt['questions'][0][0] = hosts.addr_to_ptr(hit_str) |
410 | + |
411 | + outbuf = DNS.Serialize(pckt).get_packet() |
412 | + self.clisock.sendto(outbuf, (self.server_ip, |
413 | + self.server_port)) |
414 | + |
415 | + self.add_query(self.server_ip, self.server_port, |
416 | + self.query_id, query) |
417 | + |
418 | + def handle_response(self, packet, sender): |
419 | + """Handle DNS response from upstream server.""" |
420 | + if packet['qdcount'] == 0: |
421 | + logging.warn('Bad response from upstream server: %s', |
422 | + pprint.pformat(packet)) |
423 | + return |
424 | + |
425 | + # Find original query |
426 | + query_id_o = packet['id'] |
427 | + query_o = self.find_query(sender[0], sender[1], |
428 | + query_id_o) |
429 | + if query_o and packet['qdcount'] > 0: |
430 | + qname = packet['questions'][0][0] |
431 | + qtype = packet['questions'][0][1] |
432 | + send_reply = True |
433 | + query_again = False |
434 | + hit_found = False |
435 | + packet_o = query_o[0] |
436 | + # Replace with the original query id |
437 | + packet['id'] = packet_o['id'] |
438 | + |
439 | + if qtype == DNS.Type.HIP and query_o[3] in (DNS.Type.A, |
440 | + DNS.Type.AAAA): |
441 | + # Restore qtype |
442 | + packet['questions'][0][1] = query_o[3] |
443 | + self.hip_lookup(packet) |
444 | + if packet['ancount'] > 0: |
445 | + hit_found = True |
446 | + |
447 | + if (not self.prefix or |
448 | + (hit_found and not (self.getaaaa(qname) or |
449 | + self.geta(qname)))): |
450 | + query_again = True |
451 | + send_reply = False |
452 | + elif self.prefix: |
453 | + hit_found = True |
454 | + packet['questions'][0][0] = ( |
455 | + self.prefix + packet['questions'][0][0]) |
456 | + for answer in packet['answers']: |
457 | + answer[0] = self.prefix + answer[0] |
458 | + |
459 | + elif qtype in (DNS.Type.A, DNS.Type.AAAA): |
460 | + hit = self.getaaaa_hit(qname) |
461 | + ip6 = self.getaaaa(qname) |
462 | + ip4 = self.geta(qname) |
463 | + for answer in packet['answers']: |
464 | + if answer[1] in (DNS.Type.A, DNS.Type.AAAA): |
465 | + self.cache_name(qname, answer[4], |
466 | + answer[3]) |
467 | + |
468 | + if hit is not None: |
469 | + for answer in packet['answers']: |
470 | + if (answer[1] == DNS.Type.A or |
471 | + (answer[1] == DNS.Type.AAAA and not |
472 | + hosts.valid_hit(answer[4]))): |
473 | + add_hit_ip_map(hit[0], answer[4]) |
474 | + |
475 | + # Reply with HIT/LSI once it's been mapped |
476 | + # to an IP |
477 | + if ip6 is None and ip4 is None: |
478 | + if (packet_o['ancount'] == 0 and |
479 | + not self.prefix): |
480 | + # No LSI available. Return IPv4 |
481 | + tmp = packet['answers'] |
482 | + packet = packet_o |
483 | + packet['answers'] = tmp |
484 | + packet['ancount'] = len( |
485 | + packet['answers']) |
486 | + else: |
487 | + packet = packet_o |
488 | + if self.prefix: |
489 | + packet['questions'][0][0] = ( |
490 | + self.prefix + packet['questions'][0][0]) |
491 | + for answer in packet['answers']: |
492 | + answer[0] = (self.prefix + |
493 | + answer[0]) |
494 | + |
495 | + else: |
496 | + send_reply = False |
497 | + |
498 | + elif query_o[3] == 0: |
499 | + # Prefix is in use |
500 | + # IP was queried for cache only |
501 | + send_reply = False |
502 | + |
503 | + elif qtype == DNS.Type.PTR and isinstance(query_o[3], |
504 | + str): |
505 | + packet['questions'][0][0] = query_o[3] |
506 | + for answer in packet['answers']: |
507 | + answer[0] = query_o[3] |
508 | + |
509 | + if query_again: |
510 | + if hit_found: |
511 | + qtypes = [DNS.Type.AAAA, DNS.Type.A] |
512 | + pckt = copy.deepcopy(packet) |
513 | + else: |
514 | + qtypes = [query_o[3]] |
515 | + pckt = copy.copy(packet) |
516 | + |
517 | + pckt['qr'] = 0 |
518 | + pckt['answers'] = [] |
519 | + pckt['ancount'] = 0 |
520 | + pckt['nslist'] = [] |
521 | + pckt['nscount'] = 0 |
522 | + pckt['additional'] = [] |
523 | + pckt['arcount'] = 0 |
524 | + for qtype in qtypes: |
525 | + if self.prefix: |
526 | + query = (packet, query_o[1], query_o[2], |
527 | + 0) |
528 | + else: |
529 | + query = (packet, query_o[1], query_o[2], |
530 | + qtype) |
531 | + |
532 | + self.query_id = (self.query_id % 65535) + 1 |
533 | + pckt['id'] = self.query_id |
534 | + pckt['questions'][0][1] = qtype |
535 | + outbuf = DNS.Serialize(pckt).get_packet() |
536 | + self.clisock.sendto(outbuf, (self.server_ip, |
537 | + self.server_port)) |
538 | + self.add_query(self.server_ip, |
539 | + self.server_port, |
540 | + self.query_id, query) |
541 | + |
542 | + packet['questions'][0][1] = query_o[3] |
543 | + |
544 | + if send_reply: |
545 | + outbuf = DNS.Serialize(packet).get_packet() |
546 | + self.servsock.sendto(outbuf, (query_o[1], |
547 | + query_o[2])) |
548 | + |
549 | def mainloop(self, unused_args): |
550 | """HIP DNS proxy main loop.""" |
551 | - connected = False |
552 | - |
553 | logging.info('Dns proxy for HIP started') |
554 | |
555 | self.parameter_defaults() |
556 | @@ -460,16 +696,15 @@ |
557 | # avoid problems with other dns forwarders (e.g. dnsmasq) |
558 | os.system('ifconfig lo:53 %s' % (self.bind_ip,)) |
559 | |
560 | - servsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
561 | + self.servsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
562 | try: |
563 | - servsock.bind((self.bind_ip, self.bind_port)) |
564 | + self.servsock.bind((self.bind_ip, self.bind_port)) |
565 | except socket.error: |
566 | logging.error('Port %d already in use. See HOWTO.', |
567 | self.bind_port) |
568 | return |
569 | |
570 | - |
571 | - servsock.settimeout(self.app_timeout) |
572 | + self.servsock.settimeout(self.app_timeout) |
573 | |
574 | self.hosts = [] |
575 | if self.hostsnames: |
576 | @@ -484,8 +719,6 @@ |
577 | |
578 | self.write_local_hits_to_hosts() |
579 | |
580 | - query_id = 1 |
581 | - |
582 | util.init_wantdown() # Initialize signal handler for shutdown |
583 | util.init_hup() # Initialize signal handler for reload |
584 | |
585 | @@ -496,232 +729,54 @@ |
586 | self.server_ip = self.resolvconf.nameserver |
587 | |
588 | if util.wanthup(): |
589 | - logging.info('Received SIGHUP. Picking %s as new upstream ' |
590 | - 'DNS server', self.server_ip) |
591 | + logging.info('Received SIGHUP. Picking new upstream server') |
592 | + self.server_ip = self.resolvconf.nameserver |
593 | util.wanthup(False) |
594 | |
595 | - logging.info('Connecting to upstream DNS server...') |
596 | + logging.info('Connecting to upstream DNS server %s ...', |
597 | + self.server_ip) |
598 | if ':' not in self.server_ip: |
599 | server_family = socket.AF_INET |
600 | else: |
601 | server_family = socket.AF_INET6 |
602 | - clisock = socket.socket(server_family, socket.SOCK_DGRAM) |
603 | - clisock.settimeout(self.dns_timeout) |
604 | + |
605 | + self.clisock = socket.socket(server_family, socket.SOCK_DGRAM) |
606 | + self.clisock.settimeout(self.dns_timeout) |
607 | try: |
608 | - clisock.connect((self.server_ip, self.server_port)) |
609 | - connected = True |
610 | + self.clisock.connect((self.server_ip, self.server_port)) |
611 | + self.connected = True |
612 | logging.debug('... connected!') |
613 | self.resolvconf.nameserver = self.bind_ip |
614 | except socket.error: |
615 | logging.error('Connecting to upstream DNS server failed!') |
616 | time.sleep(3) |
617 | - connected = False |
618 | + self.connected = False |
619 | |
620 | - while connected and (not util.wantdown()) and (not util.wanthup()): |
621 | + while self.connected and (not util.wantdown()) and ( |
622 | + not util.wanthup()): |
623 | try: |
624 | self.hosts_recheck() |
625 | |
626 | - if connected: |
627 | - rlist, _, _ = select.select([servsock, clisock], [], [], |
628 | - 5.0) |
629 | + if self.connected: |
630 | + rlist, _, _ = select.select([self.servsock, |
631 | + self.clisock], |
632 | + [], [], 5.0) |
633 | else: |
634 | - rlist, _, _ = select.select([servsock], [], [], 5.0) |
635 | + rlist, _, _ = select.select([self.servsock], |
636 | + [], [], 5.0) |
637 | + |
638 | self.clean_queries() |
639 | - if servsock in rlist: # Incoming DNS request |
640 | - inbuf, from_a = servsock.recvfrom(2048) |
641 | - |
642 | - packet = DeSerialize(inbuf).get_dict() |
643 | - qtype = packet['questions'][0][1] |
644 | - |
645 | - sent_answer = False |
646 | - |
647 | - if qtype in (Type.A, Type.AAAA, Type.PTR): |
648 | - if self.hip_cache_lookup(packet): |
649 | - try: |
650 | - outbuf = Serialize(packet).get_packet() |
651 | - servsock.sendto(outbuf, from_a) |
652 | - sent_answer = True |
653 | - except socket.error: |
654 | - logging.exception('Exception:') |
655 | - elif (self.prefix and |
656 | - packet['questions'][0][0].startswith( |
657 | - self.prefix)): |
658 | - # Query with HIP prefix for unsupported RR type. |
659 | - # Send empty response. |
660 | - packet['qr'] = 1 |
661 | - try: |
662 | - outbuf = Serialize(packet).get_packet() |
663 | - servsock.sendto(outbuf, from_a) |
664 | - sent_answer = True |
665 | - except socket.error: |
666 | - logging.exception('Exception:') |
667 | - |
668 | - if connected and not sent_answer: |
669 | - logging.info('Query type %d for %s from %s', |
670 | - qtype, packet['questions'][0][0], |
671 | - (self.server_ip, self.server_port)) |
672 | - |
673 | - query = (packet, from_a[0], from_a[1], qtype) |
674 | - # FIXME: Should randomize for security |
675 | - query_id = (query_id % 65535) + 1 |
676 | - pckt = copy.copy(packet) |
677 | - pckt['id'] = query_id |
678 | - if ((qtype == Type.AAAA or |
679 | - (qtype == Type.A and |
680 | - not self.disable_lsi)) and |
681 | - not is_reverse_hit_query( |
682 | - packet['questions'][0][0])): |
683 | - |
684 | - if not self.prefix: |
685 | - pckt['questions'][0][1] = Type.HIP |
686 | - if (self.prefix and |
687 | - pckt['questions'][0][0].startswith( |
688 | - self.prefix)): |
689 | - pckt['questions'][0][0] = pckt[ |
690 | - 'questions'][0][0][len(self.prefix):] |
691 | - pckt['questions'][0][1] = Type.HIP |
692 | - |
693 | - if qtype == Type.PTR and not self.disable_lsi: |
694 | - qname = packet['questions'][0][0] |
695 | - addr_str = hosts.ptr_to_addr(qname) |
696 | - if (addr_str is not None and |
697 | - hosts.valid_lsi(addr_str)): |
698 | - query = (packet, from_a[0], from_a[1], |
699 | - qname) |
700 | - hit_str = lsi_to_hit(addr_str) |
701 | - if hit_str is not None: |
702 | - pckt['questions'][0][0] = \ |
703 | - hosts.addr_to_ptr(hit_str) |
704 | - |
705 | - outbuf = Serialize(pckt).get_packet() |
706 | - clisock.sendto(outbuf, (self.server_ip, |
707 | - self.server_port)) |
708 | - |
709 | - self.add_query(self.server_ip, self.server_port, |
710 | - query_id, query) |
711 | - |
712 | - if connected and clisock in rlist: # Incoming DNS reply |
713 | - inbuf, from_a = clisock.recvfrom(2048) |
714 | + if self.servsock in rlist: |
715 | + payload, sender = self.servsock.recvfrom(2048) |
716 | + packet = DNS.DeSerialize(payload).get_dict() |
717 | + self.handle_query(packet, sender) |
718 | + |
719 | + if self.connected and self.clisock in rlist: |
720 | + payload, sender = self.clisock.recvfrom(2048) |
721 | logging.info('Packet from DNS server %d bytes from %s', |
722 | - len(inbuf), from_a) |
723 | - packet = DeSerialize(inbuf).get_dict() |
724 | - |
725 | - # Find original query |
726 | - query_id_o = packet['id'] |
727 | - query_o = self.find_query(from_a[0], from_a[1], |
728 | - query_id_o) |
729 | - if query_o: |
730 | - qname = packet['questions'][0][0] |
731 | - qtype = packet['questions'][0][1] |
732 | - send_reply = True |
733 | - query_again = False |
734 | - hit_found = False |
735 | - packet_o = query_o[0] |
736 | - # Replace with the original query id |
737 | - packet['id'] = packet_o['id'] |
738 | - |
739 | - if qtype == Type.HIP and query_o[3] in (Type.A, |
740 | - Type.AAAA): |
741 | - # Restore qtype |
742 | - packet['questions'][0][1] = query_o[3] |
743 | - self.hip_lookup(packet) |
744 | - if packet['ancount'] > 0: |
745 | - hit_found = True |
746 | - if (not self.prefix or |
747 | - (hit_found and not (self.getaaaa(qname) or |
748 | - self.geta(qname)))): |
749 | - query_again = True |
750 | - send_reply = False |
751 | - elif self.prefix: |
752 | - hit_found = True |
753 | - packet['questions'][0][0] = ( |
754 | - self.prefix + packet['questions'][0][0]) |
755 | - for answer in packet['answers']: |
756 | - answer[0] = self.prefix + answer[0] |
757 | - |
758 | - elif qtype in (Type.A, Type.AAAA): |
759 | - hit = self.getaaaa_hit(qname) |
760 | - ip6 = self.getaaaa(qname) |
761 | - ip4 = self.geta(qname) |
762 | - for answer in packet['answers']: |
763 | - if answer[1] in (Type.A, Type.AAAA): |
764 | - self.cache_name(qname, answer[4], |
765 | - answer[3]) |
766 | - if hit is not None: |
767 | - for answer in packet['answers']: |
768 | - if (answer[1] == Type.A or |
769 | - (answer[1] == Type.AAAA and not |
770 | - hosts.valid_hit(answer[4]))): |
771 | - add_hit_ip_map(hit[0], answer[4]) |
772 | - # Reply with HIT/LSI once it's been mapped |
773 | - # to an IP |
774 | - if ip6 is None and ip4 is None: |
775 | - if (packet_o['ancount'] == 0 and |
776 | - not self.prefix): |
777 | - # No LSI available. Return IPv4 |
778 | - tmp = packet['answers'] |
779 | - packet = packet_o |
780 | - packet['answers'] = tmp |
781 | - packet['ancount'] = len( |
782 | - packet['answers']) |
783 | - else: |
784 | - packet = packet_o |
785 | - if self.prefix: |
786 | - packet['questions'][0][0] = \ |
787 | - (self.prefix + |
788 | - packet['questions'][0][0]) |
789 | - for answer in packet['answers']: |
790 | - answer[0] = (self.prefix + |
791 | - answer[0]) |
792 | - else: |
793 | - send_reply = False |
794 | - elif query_o[3] == 0: |
795 | - # Prefix is in use |
796 | - # IP was queried for cache only |
797 | - send_reply = False |
798 | - |
799 | - elif qtype == Type.PTR and isinstance(query_o[3], |
800 | - str): |
801 | - packet['questions'][0][0] = query_o[3] |
802 | - for answer in packet['answers']: |
803 | - answer[0] = query_o[3] |
804 | - |
805 | - if query_again: |
806 | - if hit_found: |
807 | - qtypes = [Type.AAAA, Type.A] |
808 | - pckt = copy.deepcopy(packet) |
809 | - else: |
810 | - qtypes = [query_o[3]] |
811 | - pckt = copy.copy(packet) |
812 | - pckt['qr'] = 0 |
813 | - pckt['answers'] = [] |
814 | - pckt['ancount'] = 0 |
815 | - pckt['nslist'] = [] |
816 | - pckt['nscount'] = 0 |
817 | - pckt['additional'] = [] |
818 | - pckt['arcount'] = 0 |
819 | - for qtype in qtypes: |
820 | - if self.prefix: |
821 | - query = (packet, query_o[1], query_o[2], |
822 | - 0) |
823 | - else: |
824 | - query = (packet, query_o[1], query_o[2], |
825 | - qtype) |
826 | - query_id = (query_id % 65535) + 1 |
827 | - pckt['id'] = query_id |
828 | - pckt['questions'][0][1] = qtype |
829 | - outbuf = Serialize(pckt).get_packet() |
830 | - clisock.sendto(outbuf, (self.server_ip, |
831 | - self.server_port)) |
832 | - self.add_query(self.server_ip, |
833 | - self.server_port, |
834 | - query_id, query) |
835 | - packet['questions'][0][1] = query_o[3] |
836 | - |
837 | - if send_reply: |
838 | - outbuf = Serialize(packet).get_packet() |
839 | - servsock.sendto(outbuf, (query_o[1], |
840 | - query_o[2])) |
841 | + len(payload), sender) |
842 | + packet = DNS.DeSerialize(payload).get_dict() |
843 | + self.handle_response(packet, sender) |
844 | except (select.error, OSError), exc: |
845 | if exc[0] == errno.EINTR: |
846 | pass |
847 | @@ -729,7 +784,7 @@ |
848 | logging.exception('Exception:') |
849 | except socket.error, exc: |
850 | logging.info('Connection to upstream DNS server lost') |
851 | - connected = False |
852 | + self.connected = False |
853 | |
854 | logging.info('Wants down') |
855 | self.resolvconf.restore() |
856 | |
857 | === added file 'tools/hipdnsproxy/dnsproxy_test.py' |
858 | --- tools/hipdnsproxy/dnsproxy_test.py 1970-01-01 00:00:00 +0000 |
859 | +++ tools/hipdnsproxy/dnsproxy_test.py 2013-10-11 15:24:31 +0000 |
860 | @@ -0,0 +1,182 @@ |
861 | +#! /usr/bin/env python |
862 | + |
863 | +# Copyright (c) 2012 Aalto University and RWTH Aachen University. |
864 | +# |
865 | +# Permission is hereby granted, free of charge, to any person |
866 | +# obtaining a copy of this software and associated documentation |
867 | +# files (the "Software"), to deal in the Software without |
868 | +# restriction, including without limitation the rights to use, |
869 | +# copy, modify, merge, publish, distribute, sublicense, and/or sell |
870 | +# copies of the Software, and to permit persons to whom the |
871 | +# Software is furnished to do so, subject to the following |
872 | +# conditions: |
873 | +# |
874 | +# The above copyright notice and this permission notice shall be |
875 | +# included in all copies or substantial portions of the Software. |
876 | +# |
877 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
878 | +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
879 | +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
880 | +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
881 | +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
882 | +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
883 | +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
884 | +# OTHER DEALINGS IN THE SOFTWARE. |
885 | + |
886 | +# pylint: disable-msg=C0111,R0903,C0103,R0904 |
887 | + |
888 | +import DNS |
889 | +import dnsproxy |
890 | +import logging |
891 | +import subprocess |
892 | +import StringIO |
893 | +import unittest |
894 | + |
895 | + |
896 | +# Template DNS packet for used as basis of other packets in testing (rfc2929) |
897 | +TEMPLATE_PACKET = {'aa': 0, # authoritativy answer |
898 | + 'additional': [], # additional records |
899 | + 'ancount': 0, # answer count, len(answers) |
900 | + 'answers': [], # answer records |
901 | + 'arcount': 0, # additional count, len(additional) |
902 | + 'id': 0, # Transaction id |
903 | + 'nscount': 0, # ns count, len(nslist) |
904 | + 'nslist': [], # NS records |
905 | + 'opcode': 0, # 0 = query (rfc1035) |
906 | + # 1 = inverse query (rfc1035) |
907 | + # 2 = status (rfc1035) |
908 | + # 4 = notify (rfc1996) |
909 | + # 5 = update (rfc2136) |
910 | + 'qdcount': 0, # query count, len(questions) |
911 | + 'qr': 0, # 0 = query, 1 = response |
912 | + 'questions': [], # questions |
913 | + 'ra': 1, # recursion available flag |
914 | + 'rcode': 0, # 0 = no error (rfc1035) |
915 | + # 1 = format error (rfc1035) |
916 | + # 2 = server failure (rfc 1035) |
917 | + # 3 = nxdomain (rfc1035) |
918 | + # 4 = not implemented (rfc1035) |
919 | + # 5 = query refused (rfc1035) |
920 | + # 6 = yxdomain (rfc2136) |
921 | + # 7 = yxrrset (rfc2136) |
922 | + # 8 = nxrrset (rfc2136) |
923 | + # 9 = not authoritative (rfc2136) |
924 | + # 10 = not in zone (rfc2136) |
925 | + # 16 = bad tsig (rfc2845) |
926 | + # 17 = bad key (rfc2845) |
927 | + # 18 = bad time (rfc2845) |
928 | + # 19 = bad tkey mode (rfc2930) |
929 | + # 20 = bad key name (rfc2930) |
930 | + # 21 = bad algorithm (rfc2930) |
931 | + 'rd': 0, # recursion desired flag |
932 | + 'tc': 0, # message truncated flag |
933 | + 'z': 0} # no longer in use, ignore (rfc2929) |
934 | +SIMPLE_RESPONSE = TEMPLATE_PACKET.copy() |
935 | +SIMPLE_RESPONSE.update({'ancount': 1, |
936 | + 'answers': (('example.com', DNS.Type.A, DNS.Class.IN, |
937 | + 60, '127.0.0.1'),), |
938 | + 'qdcount': 1, |
939 | + 'questions': (('example.com', DNS.Type.A, |
940 | + DNS.Class.IN),), |
941 | + 'qr': 1}) |
942 | + |
943 | +SIMPLE_QUERY = TEMPLATE_PACKET.copy() |
944 | +SIMPLE_QUERY.update({'qdcount': 1, |
945 | + 'questions': (('example.com', DNS.Type.A, |
946 | + DNS.Class.IN),)}) |
947 | + |
948 | + |
949 | +HIT = '2001:1b:a9be:c6a6:34e5:8361:c07f:a990' |
950 | +LSI = '1.0.0.1' |
951 | + |
952 | + |
953 | +class MockSocket(object): |
954 | + def sendto(self, payload, sender): |
955 | + pass |
956 | + |
957 | + |
958 | +class MockFile(object): |
959 | + def __init__(self, exc=None, payload=None): |
960 | + self.exc = exc |
961 | + self.payload = StringIO.StringIO(payload) |
962 | + |
963 | + def __iter__(self): |
964 | + if self.exc is not None: |
965 | + return self |
966 | + return self.payload.__iter__() |
967 | + |
968 | + def next(self): |
969 | + if self.exc is not None: |
970 | + raise self.exc |
971 | + raise StopIteration |
972 | + |
973 | + |
974 | +class MockPopen(object): |
975 | + def __init__(self, exc=None, stdout=None, returncode=0): |
976 | + if exc is not None: |
977 | + self.stdout = MockFile(exc=exc, payload=stdout) |
978 | + else: |
979 | + self.stdout = StringIO.StringIO(stdout) |
980 | + |
981 | + self.returncode = returncode |
982 | + |
983 | + |
984 | +class DNSProxyTest(unittest.TestCase): |
985 | + def setUp(self): |
986 | + logging.basicConfig(level=logging.DEBUG) |
987 | + self.dnsproxy = dnsproxy.DNSProxy() |
988 | + self.dnsproxy.hosts = [] |
989 | + self.dnsproxy.clisock = MockSocket() |
990 | + self.dnsproxy.servsock = MockSocket() |
991 | + |
992 | + def test_handle_response(self): |
993 | + self.dnsproxy.add_query('127.0.0.1', 53, 0, |
994 | + (SIMPLE_QUERY, '127.0.0.1', 53, |
995 | + SIMPLE_QUERY['questions'][0][1])) |
996 | + self.dnsproxy.handle_response(SIMPLE_RESPONSE, ('127.0.0.1', 53)) |
997 | + |
998 | + def test_handle_query(self): |
999 | + self.dnsproxy.handle_query(SIMPLE_QUERY, ('127.0.0.1', 53)) |
1000 | + |
1001 | + def test_is_reverse_hit_query(self): |
1002 | + self.assertFalse(dnsproxy.is_reverse_hit_query('::1')) |
1003 | + name = ('8.e.b.8.b.3.c.9.1.a.0.c.e.e.2.c.c.e.d.0.9.c.9.a.e.1.0.0.1.0.' |
1004 | + '0.2.hit-to-ip.infrahip.net') |
1005 | + self.assertTrue(dnsproxy.is_reverse_hit_query(name)) |
1006 | + |
1007 | + def test_hit_to_lsi(self): |
1008 | + subprocess.Popen = lambda *x, **y: MockPopen(exc=IOError('hit_to_lsi')) |
1009 | + lsi = dnsproxy.hit_to_lsi(HIT) |
1010 | + self.assertIsNone(lsi) |
1011 | + |
1012 | + subprocess.Popen = lambda *x, **y: MockPopen(returncode=254) |
1013 | + lsi = dnsproxy.hit_to_lsi(HIT) |
1014 | + self.assertIsNone(lsi) |
1015 | + |
1016 | + subprocess.Popen = lambda *x, **y: MockPopen(stdout=LSI) |
1017 | + lsi = dnsproxy.hit_to_lsi(HIT) |
1018 | + self.assertEqual(lsi, LSI) |
1019 | + |
1020 | + def test_lsi_to_hit(self): |
1021 | + subprocess.Popen = lambda *x, **y: MockPopen(exc=IOError('lsi_to_hit')) |
1022 | + hit = dnsproxy.lsi_to_hit(LSI) |
1023 | + self.assertIsNone(hit) |
1024 | + |
1025 | + subprocess.Popen = lambda *x, **y: MockPopen(returncode=254) |
1026 | + hit = dnsproxy.lsi_to_hit(LSI) |
1027 | + self.assertIsNone(hit) |
1028 | + |
1029 | + subprocess.Popen = lambda *x, **y: MockPopen(stdout=HIT) |
1030 | + hit = dnsproxy.lsi_to_hit(LSI) |
1031 | + self.assertEqual(hit, HIT) |
1032 | + |
1033 | + def test_add_hit_ip_map(self): |
1034 | + # pylint: disable-msg=W0613 |
1035 | + def check_call(*x, **y): |
1036 | + raise subprocess.CalledProcessError(254, 'hipconf daemon') |
1037 | + subprocess.check_call = check_call |
1038 | + self.assertIsNone(dnsproxy.add_hit_ip_map(HIT, LSI)) |
1039 | + |
1040 | + |
1041 | +if __name__ == '__main__': |
1042 | + unittest.main() |
1043 | |
1044 | === modified file 'tools/hipdnsproxy/hipdnsproxy.in' |
1045 | --- tools/hipdnsproxy/hipdnsproxy.in 2013-02-22 15:16:12 +0000 |
1046 | +++ tools/hipdnsproxy/hipdnsproxy.in 2013-10-11 15:24:31 +0000 |
1047 | @@ -39,6 +39,8 @@ |
1048 | optparser.add_option('-p', '--serverport', dest='server_port', |
1049 | action='store', type='int', default=None, |
1050 | help='DNS server port') |
1051 | + optparser.add_option('-S', '--syslog', dest='syslog', action='store_true', |
1052 | + default=False, help='Log to syslog') |
1053 | optparser.add_option('-t', '--dns-timeout', dest='dns_timeout', |
1054 | action='store', type='float', default=2.0, |
1055 | help='DNS timeout') |
1056 | @@ -58,15 +60,19 @@ |
1057 | |
1058 | (options, args) = optparser.parse_args() |
1059 | |
1060 | + logging.getLogger().setLevel(options.loglevel) |
1061 | + |
1062 | child = False |
1063 | if (options.background): |
1064 | child = util.daemonize() |
1065 | else: |
1066 | - logging.getLogger().setLevel(options.loglevel) |
1067 | - loghandler = logging.StreamHandler(sys.stderr) |
1068 | - loghandler.setFormatter(logging.Formatter( |
1069 | - '%(asctime)s %(levelname)-8s %(message)s')) |
1070 | - logging.getLogger().addHandler(loghandler) |
1071 | + if options.syslog: |
1072 | + util.log2syslog() |
1073 | + else: |
1074 | + loghandler = logging.StreamHandler(sys.stderr) |
1075 | + loghandler.setFormatter(logging.Formatter( |
1076 | + '%(asctime)s %(levelname)-8s %(message)s')) |
1077 | + logging.getLogger().addHandler(loghandler) |
1078 | |
1079 | dnsp = dnsproxy.DNSProxy(bind_ip=options.bind_ip, |
1080 | bind_port=options.bind_port, |
1081 | |
1082 | === modified file 'tools/hipdnsproxy/resolvconf.py' |
1083 | --- tools/hipdnsproxy/resolvconf.py 2013-08-11 15:03:36 +0000 |
1084 | +++ tools/hipdnsproxy/resolvconf.py 2013-10-11 15:24:31 +0000 |
1085 | @@ -31,6 +31,7 @@ |
1086 | import os |
1087 | import subprocess |
1088 | import tempfile |
1089 | +import time |
1090 | |
1091 | |
1092 | class FragmentFile(object): |
1093 | @@ -40,8 +41,7 @@ |
1094 | def __init__(self, fragments): |
1095 | self.fragments = fragments |
1096 | self.current = None |
1097 | - if self.fragments: |
1098 | - self.current = open(self.fragments.pop(0), 'rb') |
1099 | + self.nextfragment() |
1100 | |
1101 | def __iter__(self): |
1102 | return self |
1103 | @@ -52,6 +52,23 @@ |
1104 | def __exit__(self, exc_type, exc_value, trackeback): |
1105 | pass |
1106 | |
1107 | + def nextfragment(self): |
1108 | + """Advance current fragment. Returns True when successful.""" |
1109 | + if self.fragments: |
1110 | + fragment = self.fragments.pop(0) |
1111 | + retries = 3 |
1112 | + while retries > 0: |
1113 | + try: |
1114 | + self.current = open(fragment, 'rb') |
1115 | + return True |
1116 | + except IOError, ioe: |
1117 | + if ioe.errno != errno.ENOENT: |
1118 | + raise |
1119 | + time.sleep(0.1) |
1120 | + retries -= 1 |
1121 | + self.current = None |
1122 | + return False |
1123 | + |
1124 | def next(self): |
1125 | """Returns next line in virtual file.""" |
1126 | if self.current is None: |
1127 | @@ -60,8 +77,7 @@ |
1128 | try: |
1129 | return self.current.next() |
1130 | except StopIteration, sie: |
1131 | - if self.fragments: |
1132 | - self.current = open(self.fragments.pop(0), 'rb') |
1133 | + if self.nextfragment(): |
1134 | return self.next() |
1135 | else: |
1136 | raise sie |
1137 | @@ -95,10 +111,10 @@ |
1138 | self.config = None |
1139 | |
1140 | if self.using_resolvconf and isinstance(ResolvConf.FRAGMENT_DIR, list): |
1141 | + # pylint: disable-msg=W0141 |
1142 | ResolvConf.FRAGMENT_DIR = filter(os.path.exists, |
1143 | ResolvConf.FRAGMENT_DIR)[0] |
1144 | |
1145 | - |
1146 | def realpath(self): |
1147 | """Return the real path to the actual resolv.conf.""" |
1148 | return os.path.realpath(self.path) |
1149 | @@ -126,13 +142,13 @@ |
1150 | |
1151 | frags = [x.strip() for x in out] |
1152 | |
1153 | - return [os.path.join(ResolvConf.FRAGMENT_DIR, x) for x in frags] |
1154 | + return [os.path.join(ResolvConf.FRAGMENT_DIR, x) |
1155 | + for x in frags] |
1156 | except IOError, ioe: |
1157 | if ioe.errno != errno.EINTR: |
1158 | raise |
1159 | retries -= 1 |
1160 | |
1161 | - |
1162 | @classmethod |
1163 | def resolvconf_add(cls, ifile): |
1164 | """Pass new fragment to resolvconf(8).""" |
1165 | |
1166 | === modified file 'tools/hipdnsproxy/util.py' |
1167 | --- tools/hipdnsproxy/util.py 2013-07-11 15:08:01 +0000 |
1168 | +++ tools/hipdnsproxy/util.py 2013-10-11 15:24:31 +0000 |
1169 | @@ -87,6 +87,7 @@ |
1170 | """Check if SIGINT or SIGTERM has been received.""" |
1171 | return __FLAGS['down'] |
1172 | |
1173 | + |
1174 | def wanthup(reset=None): |
1175 | """Check if SIGHUP has been received.""" |
1176 | previous = __FLAGS['hup'] |
1177 | @@ -173,6 +174,15 @@ |
1178 | return '%dd%02dh%02dm%02ds' % (days, hours, mins, secs) |
1179 | |
1180 | |
1181 | +def log2syslog(): |
1182 | + """Configure logging to send messages to syslog.""" |
1183 | + loghandler = logging.handlers.SysLogHandler(address='/dev/log', |
1184 | + facility=logging.handlers.SysLogHandler.LOG_DAEMON) |
1185 | + loghandler.setFormatter(logging.Formatter( |
1186 | + 'hipdnsproxy[%(process)s] %(levelname)-8s %(message)s')) |
1187 | + logging.getLogger().addHandler(loghandler) |
1188 | + |
1189 | + |
1190 | def daemonize(): |
1191 | """Daemonize current process.""" |
1192 | pid = os.fork() |
1193 | @@ -193,11 +203,7 @@ |
1194 | if pid: |
1195 | sys.exit(0) |
1196 | |
1197 | - loghandler = logging.handlers.SysLogHandler(address='/dev/log', |
1198 | - facility=logging.handlers.SysLogHandler.LOG_DAEMON) |
1199 | - loghandler.setFormatter(logging.Formatter( |
1200 | - 'hipdnsproxy[%(process)s] %(levelname)-8s %(message)s')) |
1201 | - logging.getLogger().addHandler(loghandler) |
1202 | + log2syslog() |
1203 | |
1204 | sys.stdout.flush() |
1205 | sys.stderr.flush() |
Nice work! I have been testing this and it seems to work fine especially after the last fix in 6441.