Merge lp:~ptman/hipl/reload into lp:hipl

Proposed by Paul Tötterman
Status: Merged
Merged at revision: 6429
Proposed branch: lp:~ptman/hipl/reload
Merge into: lp:hipl
Diff against target: 394 lines (+85/-73)
6 files modified
debian/hipl-dnsproxy.install.in (+1/-0)
debian/resolvconf/hipdnsproxy (+2/-0)
tools/hipdnsproxy/DNS/Type.py (+1/-0)
tools/hipdnsproxy/dnsproxy.py (+43/-37)
tools/hipdnsproxy/resolvconf.py (+20/-10)
tools/hipdnsproxy/util.py (+18/-26)
To merge this branch: bzr merge lp:~ptman/hipl/reload
Reviewer Review Type Date Requested Status
Miika Komu Approve
Review via email: mp+178932@code.launchpad.net

Description of the change

Implement sane SIGHUP handling (reload configs)

Fixes #1132042 and #1189371

To post a comment you must log in.
Revision history for this message
Miika Komu (miika-iki) wrote :

Tested, works. There's still some IOerror left (I'll report this separately), but I think you could fix it directly in the trunk.

review: Approve
lp:~ptman/hipl/reload updated
6425. By Paul Tötterman

Retry in case of EINTR

6426. By Paul Tötterman

Merge trunk

6427. By Paul Tötterman

Merged trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/hipl-dnsproxy.install.in'
--- debian/hipl-dnsproxy.install.in 2011-02-08 17:55:52 +0000
+++ debian/hipl-dnsproxy.install.in 2013-08-12 13:20:15 +0000
@@ -3,3 +3,4 @@
3debian/tmp/@pythondir@/DNS @pythondir@3debian/tmp/@pythondir@/DNS @pythondir@
4debian/tmp/@pythondir@/hipdnskeyparse @pythondir@4debian/tmp/@pythondir@/hipdnskeyparse @pythondir@
5debian/tmp/@pythondir@/hipdnsproxy @pythondir@5debian/tmp/@pythondir@/hipdnsproxy @pythondir@
6debian/resolvconf/hipdnsproxy etc/resolvconf/update.d
67
=== added directory 'debian/resolvconf'
=== added file 'debian/resolvconf/hipdnsproxy'
--- debian/resolvconf/hipdnsproxy 1970-01-01 00:00:00 +0000
+++ debian/resolvconf/hipdnsproxy 2013-08-12 13:20:15 +0000
@@ -0,0 +1,2 @@
1#!/bin/sh
2/bin/kill -HUP `/usr/bin/head -1 /var/run/hipdnsproxy.pid` > /dev/null 2>&1
03
=== modified file 'tools/hipdnsproxy/DNS/Type.py'
--- tools/hipdnsproxy/DNS/Type.py 2010-02-18 16:45:11 +0000
+++ tools/hipdnsproxy/DNS/Type.py 2013-08-12 13:20:15 +0000
@@ -28,6 +28,7 @@
28TXT = 16 # text strings28TXT = 16 # text strings
29AAAA = 28 # IPv6 AAAA records (RFC 1886)29AAAA = 28 # IPv6 AAAA records (RFC 1886)
30SRV = 33 # DNS RR for specifying the location of services (RFC 2782)30SRV = 33 # DNS RR for specifying the location of services (RFC 2782)
31HIP = 55 # Host Identity Protocol (RFC 5205)
3132
3233
33# Additional TYPE values from host.c source34# Additional TYPE values from host.c source
3435
=== modified file 'tools/hipdnsproxy/dnsproxy.py'
--- tools/hipdnsproxy/dnsproxy.py 2013-05-26 15:29:43 +0000
+++ tools/hipdnsproxy/dnsproxy.py 2013-08-12 13:20:15 +0000
@@ -82,7 +82,7 @@
82import hosts82import hosts
83import resolvconf83import resolvconf
84import util84import util
85from DNS import Serialize, DeSerialize85from DNS import Serialize, DeSerialize, Type
8686
8787
88DEFAULT_HOSTS = '/etc/hosts'88DEFAULT_HOSTS = '/etc/hosts'
@@ -167,7 +167,6 @@
167 self.server_port = server_port167 self.server_port = server_port
168168
169 self.resolvconf = resolvconf.ResolvConf()169 self.resolvconf = resolvconf.ResolvConf()
170 self.resolvconf.parse()
171170
172 if self.hostsnames is None:171 if self.hostsnames is None:
173 self.hostsnames = []172 self.hostsnames = []
@@ -218,8 +217,6 @@
218 env = os.environ217 env = os.environ
219 if self.server_ip is None:218 if self.server_ip is None:
220 self.server_ip = env.get('SERVER', None)219 self.server_ip = env.get('SERVER', None)
221 if self.server_ip is None:
222 self.server_ip = self.resolvconf.nameserver
223 if self.server_port is None:220 if self.server_port is None:
224 server_port = env.get('SERVERPORT', None)221 server_port = env.get('SERVERPORT', None)
225 if server_port is not None:222 if server_port is not None:
@@ -367,7 +364,7 @@
367364
368 # convert 1.2....1.0.0.1.0.0.2.ip6.arpa to a HIT and365 # convert 1.2....1.0.0.1.0.0.2.ip6.arpa to a HIT and
369 # map host name to address from cache366 # map host name to address from cache
370 if qtype == 12:367 if qtype == Type.PTR:
371 lr_ptr = None368 lr_ptr = None
372 addr_str = hosts.ptr_to_addr(qname)369 addr_str = hosts.ptr_to_addr(qname)
373 if (not self.disable_lsi and addr_str is not None and370 if (not self.disable_lsi and addr_str is not None and
@@ -387,19 +384,19 @@
387 add_hit_ip_map(lr_aaaa_hit[0], lr_a[0])384 add_hit_ip_map(lr_aaaa_hit[0], lr_a[0])
388 if lr_aaaa is not None:385 if lr_aaaa is not None:
389 add_hit_ip_map(lr_aaaa_hit[0], lr_aaaa[0])386 add_hit_ip_map(lr_aaaa_hit[0], lr_aaaa[0])
390 if qtype == 28: # 28: AAAA387 if qtype == Type.AAAA:
391 result = lr_aaaa_hit388 result = lr_aaaa_hit
392 elif qtype == 1 and not self.disable_lsi: # 1: A389 elif qtype == Type.A and not self.disable_lsi:
393 lsi = hit_to_lsi(lr_aaaa_hit[0])390 lsi = hit_to_lsi(lr_aaaa_hit[0])
394 if lsi is not None:391 if lsi is not None:
395 result = (lsi, lr_aaaa_hit[1])392 result = (lsi, lr_aaaa_hit[1])
396 elif self.prefix and packet['questions'][0][0].startswith(self.prefix):393 elif self.prefix and packet['questions'][0][0].startswith(self.prefix):
397 result = None394 result = None
398 elif qtype == 28:395 elif qtype == Type.AAAA:
399 result = lr_aaaa396 result = lr_aaaa
400 elif qtype == 1:397 elif qtype == Type.A:
401 result = lr_a398 result = lr_a
402 elif qtype == 12 and lr_ptr is not None: # 12: PTR399 elif qtype == Type.PTR and lr_ptr is not None:
403 result = (lr_ptr, self.hosts_ttl)400 result = (lr_ptr, self.hosts_ttl)
404401
405 if result is not None:402 if result is not None:
@@ -418,7 +415,7 @@
418415
419 dns_hit_found = False416 dns_hit_found = False
420 for answer in packet['answers']:417 for answer in packet['answers']:
421 if answer[1] == 55:418 if answer[1] == Type.HIP:
422 dns_hit_found = True419 dns_hit_found = True
423 break420 break
424421
@@ -429,20 +426,20 @@
429 lsi_ans = []426 lsi_ans = []
430427
431 for answer in packet['answers']:428 for answer in packet['answers']:
432 if answer[1] != 55:429 if answer[1] != Type.HIP:
433 continue430 continue
434431
435 hit = socket.inet_ntop(socket.AF_INET6, answer[7])432 hit = socket.inet_ntop(socket.AF_INET6, answer[7])
436 hit_ans.append([qname, 28, 1, answer[3], hit])433 hit_ans.append([qname, Type.AAAA, 1, answer[3], hit])
437434
438 if qtype == 1 and not self.disable_lsi:435 if qtype == Type.A and not self.disable_lsi:
439 lsi = hit_to_lsi(hit)436 lsi = hit_to_lsi(hit)
440 if lsi is not None:437 if lsi is not None:
441 lsi_ans.append([qname, 1, 1, self.hosts_ttl, lsi])438 lsi_ans.append([qname, 1, 1, self.hosts_ttl, lsi])
442439
443 self.cache_name(qname, hit, answer[3])440 self.cache_name(qname, hit, answer[3])
444441
445 if qtype == 28 and hit_found:442 if qtype == Type.AAAA and hit_found:
446 packet['answers'] = hit_ans443 packet['answers'] = hit_ans
447 elif lsi is not None:444 elif lsi is not None:
448 packet['answers'] = lsi_ans445 packet['answers'] = lsi_ans
@@ -459,10 +456,6 @@
459456
460 self.parameter_defaults()457 self.parameter_defaults()
461458
462 if self.server_ip is None:
463 logging.critical('No upstream DNS server, exiting...')
464 return
465
466 # Default virtual interface and address for dnsproxy to459 # Default virtual interface and address for dnsproxy to
467 # avoid problems with other dns forwarders (e.g. dnsmasq)460 # avoid problems with other dns forwarders (e.g. dnsmasq)
468 os.system('ifconfig lo:53 %s' % (self.bind_ip,))461 os.system('ifconfig lo:53 %s' % (self.bind_ip,))
@@ -475,7 +468,6 @@
475 self.bind_port)468 self.bind_port)
476 return469 return
477470
478 self.resolvconf.nameserver = self.bind_ip
479471
480 servsock.settimeout(self.app_timeout)472 servsock.settimeout(self.app_timeout)
481473
@@ -494,10 +486,20 @@
494486
495 query_id = 1487 query_id = 1
496488
497 util.init_wantdown()489 util.init_wantdown() # Initialize signal handler for shutdown
498 util.init_wantdown_int() # Keyboard interrupts490 util.init_hup() # Initialize signal handler for reload
499491
500 while not util.wantdown():492 while not util.wantdown():
493 self.resolvconf.parse()
494
495 if self.server_ip is None:
496 self.server_ip = self.resolvconf.nameserver
497
498 if util.wanthup():
499 logging.info('Received SIGHUP. Picking %s as new upstream '
500 'DNS server', self.server_ip)
501 util.wanthup(False)
502
501 logging.info('Connecting to upstream DNS server...')503 logging.info('Connecting to upstream DNS server...')
502 if ':' not in self.server_ip:504 if ':' not in self.server_ip:
503 server_family = socket.AF_INET505 server_family = socket.AF_INET
@@ -509,12 +511,13 @@
509 clisock.connect((self.server_ip, self.server_port))511 clisock.connect((self.server_ip, self.server_port))
510 connected = True512 connected = True
511 logging.debug('... connected!')513 logging.debug('... connected!')
514 self.resolvconf.nameserver = self.bind_ip
512 except socket.error:515 except socket.error:
513 logging.error('Connecting to upstream DNS server failed!')516 logging.error('Connecting to upstream DNS server failed!')
514 time.sleep(3)517 time.sleep(3)
515 connected = False518 connected = False
516519
517 while connected and (not util.wantdown()):520 while connected and (not util.wantdown()) and (not util.wanthup()):
518 try:521 try:
519 self.hosts_recheck()522 self.hosts_recheck()
520523
@@ -532,7 +535,7 @@
532535
533 sent_answer = False536 sent_answer = False
534537
535 if qtype in (1, 28, 12):538 if qtype in (Type.A, Type.AAAA, Type.PTR):
536 if self.hip_cache_lookup(packet):539 if self.hip_cache_lookup(packet):
537 try:540 try:
538 outbuf = Serialize(packet).get_packet()541 outbuf = Serialize(packet).get_packet()
@@ -563,21 +566,22 @@
563 query_id = (query_id % 65535) + 1566 query_id = (query_id % 65535) + 1
564 pckt = copy.copy(packet)567 pckt = copy.copy(packet)
565 pckt['id'] = query_id568 pckt['id'] = query_id
566 if ((qtype == 28 or569 if ((qtype == Type.AAAA or
567 (qtype == 1 and not self.disable_lsi)) and not570 (qtype == Type.A and
568 is_reverse_hit_query(571 not self.disable_lsi)) and
572 not is_reverse_hit_query(
569 packet['questions'][0][0])):573 packet['questions'][0][0])):
570574
571 if not self.prefix:575 if not self.prefix:
572 pckt['questions'][0][1] = 55576 pckt['questions'][0][1] = Type.HIP
573 if (self.prefix and577 if (self.prefix and
574 pckt['questions'][0][0].startswith(578 pckt['questions'][0][0].startswith(
575 self.prefix)):579 self.prefix)):
576 pckt['questions'][0][0] = pckt[580 pckt['questions'][0][0] = pckt[
577 'questions'][0][0][len(self.prefix):]581 'questions'][0][0][len(self.prefix):]
578 pckt['questions'][0][1] = 55582 pckt['questions'][0][1] = Type.HIP
579583
580 if qtype == 12 and not self.disable_lsi:584 if qtype == Type.PTR and not self.disable_lsi:
581 qname = packet['questions'][0][0]585 qname = packet['questions'][0][0]
582 addr_str = hosts.ptr_to_addr(qname)586 addr_str = hosts.ptr_to_addr(qname)
583 if (addr_str is not None and587 if (addr_str is not None and
@@ -616,7 +620,8 @@
616 # Replace with the original query id620 # Replace with the original query id
617 packet['id'] = packet_o['id']621 packet['id'] = packet_o['id']
618622
619 if qtype == 55 and query_o[3] in (1, 28):623 if qtype == Type.HIP and query_o[3] in (Type.A,
624 Type.AAAA):
620 # Restore qtype625 # Restore qtype
621 packet['questions'][0][1] = query_o[3]626 packet['questions'][0][1] = query_o[3]
622 self.hip_lookup(packet)627 self.hip_lookup(packet)
@@ -634,18 +639,18 @@
634 for answer in packet['answers']:639 for answer in packet['answers']:
635 answer[0] = self.prefix + answer[0]640 answer[0] = self.prefix + answer[0]
636641
637 elif qtype in (1, 28):642 elif qtype in (Type.A, Type.AAAA):
638 hit = self.getaaaa_hit(qname)643 hit = self.getaaaa_hit(qname)
639 ip6 = self.getaaaa(qname)644 ip6 = self.getaaaa(qname)
640 ip4 = self.geta(qname)645 ip4 = self.geta(qname)
641 for answer in packet['answers']:646 for answer in packet['answers']:
642 if answer[1] in (1, 28):647 if answer[1] in (Type.A, Type.AAAA):
643 self.cache_name(qname, answer[4],648 self.cache_name(qname, answer[4],
644 answer[3])649 answer[3])
645 if hit is not None:650 if hit is not None:
646 for answer in packet['answers']:651 for answer in packet['answers']:
647 if (answer[1] == 1 or652 if (answer[1] == Type.A or
648 (answer[1] == 28 and not653 (answer[1] == Type.AAAA and not
649 hosts.valid_hit(answer[4]))):654 hosts.valid_hit(answer[4]))):
650 add_hit_ip_map(hit[0], answer[4])655 add_hit_ip_map(hit[0], answer[4])
651 # Reply with HIT/LSI once it's been mapped656 # Reply with HIT/LSI once it's been mapped
@@ -675,14 +680,15 @@
675 # IP was queried for cache only680 # IP was queried for cache only
676 send_reply = False681 send_reply = False
677682
678 elif qtype == 12 and isinstance(query_o[3], str):683 elif qtype == Type.PTR and isinstance(query_o[3],
684 str):
679 packet['questions'][0][0] = query_o[3]685 packet['questions'][0][0] = query_o[3]
680 for answer in packet['answers']:686 for answer in packet['answers']:
681 answer[0] = query_o[3]687 answer[0] = query_o[3]
682688
683 if query_again:689 if query_again:
684 if hit_found:690 if hit_found:
685 qtypes = [28, 1]691 qtypes = [Type.AAAA, Type.A]
686 pckt = copy.deepcopy(packet)692 pckt = copy.deepcopy(packet)
687 else:693 else:
688 qtypes = [query_o[3]]694 qtypes = [query_o[3]]
689695
=== modified file 'tools/hipdnsproxy/resolvconf.py'
--- tools/hipdnsproxy/resolvconf.py 2013-05-26 15:28:42 +0000
+++ tools/hipdnsproxy/resolvconf.py 2013-08-12 13:20:15 +0000
@@ -26,6 +26,7 @@
26"""Handler for resolv.conf, including resolvconf(8)."""26"""Handler for resolv.conf, including resolvconf(8)."""
2727
28import collections28import collections
29import errno
29import logging30import logging
30import os31import os
31import subprocess32import subprocess
@@ -112,16 +113,25 @@
112113
113 @classmethod114 @classmethod
114 def fragments(cls):115 def fragments(cls):
115 proc = subprocess.Popen([ResolvConf.LIST_RECORDS],116 """Return list of resolvconf fragments."""
116 cwd=ResolvConf.FRAGMENT_DIR, stdout=subprocess.PIPE)117 retries = 3
117118 while retries > 0:
118 out = proc.stdout.readlines()119 try:
119 proc.communicate()120 proc = subprocess.Popen([ResolvConf.LIST_RECORDS],
120 proc.wait()121 cwd=ResolvConf.FRAGMENT_DIR, stdout=subprocess.PIPE)
121122
122 frags = [x.strip() for x in out]123 out = proc.stdout.readlines()
123124 proc.communicate()
124 return [os.path.join(ResolvConf.FRAGMENT_DIR, x) for x in frags]125 proc.wait()
126
127 frags = [x.strip() for x in out]
128
129 return [os.path.join(ResolvConf.FRAGMENT_DIR, x) for x in frags]
130 except IOError, ioe:
131 if ioe.errno != errno.EINTR:
132 raise
133 retries -= 1
134
125135
126 @classmethod136 @classmethod
127 def resolvconf_add(cls, ifile):137 def resolvconf_add(cls, ifile):
128138
=== modified file 'tools/hipdnsproxy/util.py'
--- tools/hipdnsproxy/util.py 2013-02-22 15:16:12 +0000
+++ tools/hipdnsproxy/util.py 2013-08-12 13:20:15 +0000
@@ -32,8 +32,7 @@
32import struct32import struct
33import sys33import sys
3434
35__FLAGS = {'down': False,35__FLAGS = {'down': False, 'hup': False}
36 'alarm': False}
3736
3837
39class Random:38class Random:
@@ -67,42 +66,35 @@
6766
68def sighandler(signum, unused_frame):67def sighandler(signum, unused_frame):
69 """A signal handler that toggles flags about received signals."""68 """A signal handler that toggles flags about received signals."""
70 if signum == signal.SIGTERM:69 if signum in (signal.SIGTERM, signal.SIGINT):
71 __FLAGS['down'] = True70 __FLAGS['down'] = True
72 if signum == signal.SIGINT:71 if signum == signal.SIGHUP:
73 __FLAGS['down'] = True72 __FLAGS['hup'] = True
74 if signum == signal.SIGALRM:
75 __FLAGS['alarm'] = True
7673
7774
78def init_wantdown():75def init_wantdown():
79 """Hook signal handler to SIGTERM."""76 """Hook signal handler to SIGTERM."""
80 signal.signal(signal.SIGTERM, sighandler)77 signal.signal(signal.SIGTERM, sighandler)
81
82
83def init_wantdown_int():
84 """Hook signal handler to SIGINT."""
85 signal.signal(signal.SIGINT, sighandler)78 signal.signal(signal.SIGINT, sighandler)
8679
8780
88def init_wantalarm():81def init_hup():
89 """Hook signal handler to SIGALRM."""82 """Hook signal handler to SIGHUP."""
90 signal.signal(signal.SIGALRM, sighandler)83 signal.signal(signal.SIGHUP, sighandler)
9184
9285
93def wantdown():86def wantdown():
94 """Check if SIGINT or SIGTERM has been received."""87 """Check if SIGINT or SIGTERM has been received."""
95 previous = __FLAGS['down']88 return __FLAGS['down']
96 __FLAGS['down'] = False89
97 return previous90def wanthup(reset=None):
9891 """Check if SIGHUP has been received."""
9992 previous = __FLAGS['hup']
100def wantalarm():93
101 """Check if SIGALRM has been received."""94 if reset is not None:
102 previous = __FLAGS['alarm']95 __FLAGS['hup'] = reset
103 __FLAGS['alarm'] = False96
104 return previous97 return previous
105
10698
107TMULT = (99TMULT = (
108 (re.compile('^(?P<tval>-?\d+(\.\d*))(s|)$', re.I), float, 1),100 (re.compile('^(?P<tval>-?\d+(\.\d*))(s|)$', re.I), float, 1),

Subscribers

People subscribed via source and target branches