aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2010-07-14 02:34:39 +0000
committerRob Austein <sra@hactrn.net>2010-07-14 02:34:39 +0000
commite54ad58d4f100acbb946b8515a5027934a64f827 (patch)
tree5e54e2849448aa5eaeaf1db6a81841a608b71ac4
parent1a98378aeb4bc172c17917b13d489f5f6ac9f385 (diff)
Fix several Linux-related problems found during last workshop:
1) rpki.https.http_listener() didn't handle .bind() failure properly, which left asyncore in a weird state. 2) On Linux, sockaddr.getaddr() can return duplicate records. This is insane, but the maintainers appear to consider this a feature, so we have to suppress it. 3) Linux's support for IPv4 mapped addresses in IPv6 is so whacked that it's impossible to tell whether a socket.bind() failure is real or an artifact of Linux's whacked API without checking the value of a magic sysctl. This is just too much fun for words, so IPv6 listeners are now disabled by default on Linux; feel free to turn them on yourself, but don't blame me if they bite you. svn path=/rpkid/rpki/https.py; revision=3399
-rw-r--r--rpkid/rpki/https.py49
1 files changed, 33 insertions, 16 deletions
diff --git a/rpkid/rpki/https.py b/rpkid/rpki/https.py
index 3a021f7d..f69759fe 100644
--- a/rpkid/rpki/https.py
+++ b/rpkid/rpki/https.py
@@ -77,8 +77,13 @@ default_tcp_port = 443
## @var enable_ipv6_servers
# Whether to enable IPv6 listeners. Enabled by default, as it should
-# be harmless. Has no effect if kernel doesn't support IPv6.
-enable_ipv6_servers = True
+# be harmless, except on Linux, where the network stack is so messed
+# up that the only way to tell the difference between "normal"
+# behavior and port conflict with another server is by checking some
+# magic file off on the /proc filesystem to find out what the
+# EADDRINUSE error code means today on this system. Maybe.
+# Has no effect if kernel doesn't support IPv6.
+enable_ipv6_servers = not sys.platform.startswith("linux")
## @var enable_ipv6_clients
# Whether to consider IPv6 addresses when making connections.
@@ -769,10 +774,11 @@ class http_listener(asyncore.dispatcher):
self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
self.bind(sockaddr)
self.listen(5)
- except (rpki.async.ExitNow, SystemExit):
- raise
except:
- self.handle_error()
+ self.log("Couldn't set up HTTP listener", rpki.log.warn)
+ rpki.log.traceback()
+ self.close()
+ raise
self.log("Listening on %r, handlers %r" % (sockaddr, handlers))
def handle_accept(self):
@@ -794,12 +800,10 @@ class http_listener(asyncore.dispatcher):
"""
Asyncore signaled an error, pass it along or log it.
"""
- if sys.exc_info()[0] is SystemExit:
- self.log("Caught SystemExit, propagating")
+ if sys.exc_info()[0] in (SystemExit, rpki.async.ExitNow):
raise
- else:
- self.log("Error in HTTP listener", rpki.log.warn)
- rpki.log.traceback()
+ self.log("Error in HTTP listener", rpki.log.warn)
+ rpki.log.traceback()
class http_client(http_stream):
"""
@@ -1145,7 +1149,7 @@ def client(msg, client_key, client_cert, server_ta, url, callback, errback):
rpki.log.debug("Scheduling connection startup for %r" % request)
rpki.async.defer(client_queues[hostport].restart)
-def server(handlers, server_key, server_cert, port, host ="", client_ta = (), dynamic_https_trust_anchor = None):
+def server(handlers, server_key, server_cert, port, host = "", client_ta = (), dynamic_https_trust_anchor = None):
"""
Run an HTTPS server and wait (forever) for connections.
"""
@@ -1156,13 +1160,26 @@ def server(handlers, server_key, server_cert, port, host ="", client_ta = (), dy
if not isinstance(client_ta, (tuple, list)):
client_ta = (client_ta,)
+ # Yes, this is sick. So is getaddrinfo() returning duplicate
+ # records, which RedHat has the gall to claim is a feature.
+ ai = []
for af in supported_address_families(enable_ipv6_servers):
try:
- for addrinfo in socket.getaddrinfo(host if host else "::" if have_ipv6 and af == socket.AF_INET6 else "0.0.0.0",
- port, af, socket.SOCK_STREAM):
- http_listener(addrinfo = addrinfo, handlers = handlers, cert = server_cert, key = server_key, ta = client_ta, dynamic_ta = dynamic_https_trust_anchor)
- except socket.gaierror, e:
- rpki.log.info("getaddrinfo() error for AF %d, host %s, port %s, skipping address family: %s" % (af, host, port, e))
+ if host:
+ h = host
+ elif have_ipv6 and af == socket.AF_INET6:
+ h = "::"
+ else:
+ h = "0.0.0.0"
+ for a in socket.getaddrinfo(h, port, af, socket.SOCK_STREAM):
+ if a not in ai:
+ ai.append(a)
+ except socket.gaierror:
+ pass
+
+ for a in ai:
+ http_listener(addrinfo = a, handlers = handlers, cert = server_cert, key = server_key, ta = client_ta, dynamic_ta = dynamic_https_trust_anchor)
+
rpki.async.event_loop()
def build_https_ta_cache(certs):