Commit d4fe4145 authored by Jim Fulton's avatar Jim Fulton

Bug fixed

- ZEO didn't work with IPv6 addrsses.
  Added IPv6 support contributed by Martin v. Lowis.
parent 88e4dc41
......@@ -164,6 +164,9 @@ http://www.zope.com/Products/ZopeProducts/ZRS. In general, it could
be used to with a system that arranges to provide hot backups of
servers in the case of failure.
If a single address resolves to multiple IPv4 or IPv6 addresses,
the client will connect to an arbitrary of these addresses.
Authentication
~~~~~~~~~~~~~~
......
......@@ -8,6 +8,9 @@
Bugs fixed
----------
- ZEO didn't work with IPv6 addrsses.
Added IPv6 support contributed by Martin v. Löwis.
- Changes in way that garage collection treats dictionaries in Python
2.7 broke the object/connection cache implementation.
(https://bugs.launchpad.net/zodb/+bug/641481)
......
......@@ -16,6 +16,7 @@ import os
import sys
import time
import random
import socket
import asyncore
import threading
import logging
......@@ -1197,3 +1198,18 @@ class MSTThread(threading.Thread):
c.close()
except:
pass
# Run IPv6 tests if V6 sockets are supported
try:
socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
except (socket.error, AttributeError):
pass
else:
class V6Setup:
def _getAddr(self):
return '::1', forker.get_port(self)
_g = globals()
for name, value in _g.items():
if isinstance(value, type) and issubclass(value, CommonSetupTearDown):
_g[name+"V6"] = type(name+"V6", (V6Setup, value), {})
......@@ -1148,13 +1148,13 @@ def client_has_newer_data_than_server():
... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
ZEO.ClientStorage CRITICAL client
Client has seen newer transactions than server!
ZEO.zrpc ERROR (...) CW: error in notifyConnected (('localhost', ...))
ZEO.zrpc ERROR (...) CW: error in notifyConnected (('127.0.0.1', ...))
Traceback (most recent call last):
...
ClientStorageError: client Client has seen newer transactions than server!
ZEO.ClientStorage CRITICAL client
Client has seen newer transactions than server!
ZEO.zrpc ERROR (...) CW: error in notifyConnected (('localhost', ...))
ZEO.zrpc ERROR (...) CW: error in notifyConnected (('127.0.0.1', ...))
Traceback (most recent call last):
...
ClientStorageError: client Client has seen newer transactions than server!
......
......@@ -188,7 +188,7 @@ class ConnectionManager(object):
if (len(addr) == 2
and isinstance(addr[0], types.StringType)
and isinstance(addr[1], types.IntType)):
return socket.AF_INET
return socket.AF_INET # also denotes IPv6
# not anything I know about
return None
......@@ -436,10 +436,25 @@ class ConnectThread(threading.Thread):
del wrappers
return 0
def _expand_addrlist(self):
for domain, (host, port) in self.addrlist:
# AF_INET really means either IPv4 or IPv6, possibly
# indirected by DNS. By design, DNS lookup is deferred
# until connections get established, so that DNS
# reconfiguration can affect failover
if domain == socket.AF_INET:
for (family, socktype, proto, cannoname, sockaddr
) in socket.getaddrinfo(host, port):
# for IPv6, drop flowinfo, and restrict addresses
# to [host]:port
yield family, sockaddr[:2]
else:
yield domain, addr
def _create_wrappers(self):
# Create socket wrappers
wrappers = {} # keys are active wrappers
for domain, addr in self.addrlist:
for domain, addr in self._expand_addrlist():
wrap = ConnectWrapper(domain, addr, self.mgr, self.client)
wrap.connect_procedure()
if wrap.state == "notified":
......
......@@ -15,6 +15,23 @@ import asyncore
import socket
import types
# _has_dualstack: True if the dual-stack sockets are supported
try:
# Check whether IPv6 sockets can be created
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
except (socket.error, AttributeError):
_has_dualstack = False
else:
# Check whether enabling dualstack (disabling v6only) works
try:
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except (socket.error, AttributeError):
_has_dualstack = False
else:
_has_dualstack = True
s.close()
del s
from ZEO.zrpc.connection import Connection
from ZEO.zrpc.log import log
import ZEO.zrpc.log
......@@ -35,6 +52,20 @@ class Dispatcher(asyncore.dispatcher):
def _open_socket(self):
if type(self.addr) == types.TupleType:
if self.addr[0] == '' and _has_dualstack:
# Wildcard listen on all interfaces, both IPv4 and
# IPv6 if possible
self.create_socket(socket.AF_INET6, socket.SOCK_STREAM)
self.socket.setsockopt(
socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
elif ':' in self.addr[0]:
self.create_socket(socket.AF_INET6, socket.SOCK_STREAM)
if _has_dualstack:
# On Linux, IPV6_V6ONLY is off by default.
# If the user explicitly asked for IPv6, don't bind to IPv4
self.socket.setsockopt(
socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, True)
else:
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
else:
self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
......@@ -56,6 +87,9 @@ class Dispatcher(asyncore.dispatcher):
log("accepted failed: %s" % msg)
return
# Drop flow-info from IPv6 addresses
addr = addr[:2]
# We could short-circuit the attempt below in some edge cases
# and avoid a log message by checking for addr being None.
# Unfortunately, our test for the code below,
......
......@@ -120,6 +120,10 @@ class SizedMessageAsyncConnection(asyncore.dispatcher):
self.__super_init(sock, map)
# asyncore overwrites addr with the getpeername result
# restore our value
self.addr = addr
def setSessionKey(self, sesskey):
log("set session key %r" % sesskey)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment