diff --git a/NEWS b/NEWS
index 8d8bdd9..4d8c01d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,19 @@
 This file describes user-visible changes in rbldnsd.
 Newer news is at the top.
 
+Next release
+
+ - fix tests for systems without ipv6 support, or when ipv6 is
+   disabled in rbldnsd at compile-time
+
+ - fix tests for API change in pydns >= 2.3.6
+
+ - It is no longer an error to request binding to a particular
+   address/port more than once. (The subsequent requests are simply
+   ignored.) (This avoids confusion on certain systems/configurations
+   where gethostbyname("localhost") can return 127.0.0.1 multiple
+   times.)
+
 0.997a (23 Jul 2013)
 
  - minor fixes/changes in packaging, no code changes.
diff --git a/rbldnsd.c b/rbldnsd.c
index abf1d01..8322bdd 100644
--- a/rbldnsd.c
+++ b/rbldnsd.c
@@ -203,10 +203,79 @@ static volatile int signalled;
 #define SIGNALLED_ZSTATS	0x10
 #define SIGNALLED_TERM		0x20
 
+static inline int sockaddr_in_equal(const struct sockaddr_in *addr1,
+                                    const struct sockaddr_in *addr2)
+{
+  return (addr1->sin_port == addr2->sin_port
+          && addr1->sin_addr.s_addr == addr2->sin_addr.s_addr);
+}
+
+#ifndef NO_IPv6
+static inline int sockaddr_in6_equal(const struct sockaddr_in6 *addr1,
+                                     const struct sockaddr_in6 *addr2)
+{
+  if (memcmp(addr1->sin6_addr.s6_addr, addr2->sin6_addr.s6_addr, 16) != 0)
+    return 0;
+  return (addr1->sin6_port == addr2->sin6_port
+          && addr1->sin6_flowinfo == addr2->sin6_flowinfo
+          && addr1->sin6_scope_id == addr2->sin6_scope_id);
+}
+#endif
+
+static inline int sockaddr_equal(const struct sockaddr *addr1,
+                                 const struct sockaddr *addr2)
+{
+  if (addr1->sa_family != addr2->sa_family)
+    return 0;
+  switch (addr1->sa_family) {
+  case AF_INET:
+    return sockaddr_in_equal((const struct sockaddr_in *)addr1,
+                             (const struct sockaddr_in *)addr2);
+#ifndef NO_IPv6
+    return sockaddr_in6_equal((const struct sockaddr_in6 *)addr1,
+                              (const struct sockaddr_in6 *)addr2);
+#endif
+    default:
+      error(0, "unknown address family (%d)", addr1->sa_family);
+  }
+}
+
+/* already_bound(addr, addrlen)
+ *
+ * Determine whether we've already bound to a particular address.
+ * This is here mostly to deal with the fact that on certain systems,
+ * gethostbyname()/getaddrinfo() can return a duplicate 127.0.0.1
+ * for 'localhost'.  See
+ *   - https://sourceware.org/bugzilla/show_bug.cgi?id=4980
+ *   - https://bugzilla.redhat.com/show_bug.cgi?id=496300
+ */
+static int already_bound(const struct sockaddr *addr, socklen_t addrlen) {
+#ifdef NO_IPv6
+  struct sockaddr_in addr_buf;
+#else
+  struct sockaddr_in6 addr_buf;
+#endif
+  struct sockaddr *boundaddr = (struct sockaddr *)&addr_buf;
+  socklen_t buflen;
+  int i;
+
+  for (i = 0; i < numsock; i++) {
+    buflen = sizeof(addr_buf);
+    if (getsockname(sock[i], boundaddr, &buflen) < 0)
+      error(errno, "getsockname failed");
+    if (buflen == addrlen && sockaddr_equal(boundaddr, addr))
+      return 1;
+  }
+  return 0;
+}
+
 #ifdef NO_IPv6
 static void newsocket(struct sockaddr_in *sin) {
   int fd;
   const char *host = ip4atos(ntohl(sin->sin_addr.s_addr));
+
+  if (already_bound((struct sockaddr *)sin, sizeof(*sin)))
+    return;
   if (numsock >= MAXSOCK)
     error(0, "too many listening sockets (%d max)", MAXSOCK);
   fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
@@ -223,6 +292,8 @@ static int newsocket(struct addrinfo *ai) {
   int fd;
   char host[NI_MAXHOST], serv[NI_MAXSERV];
 
+  if (already_bound(ai->ai_addr, ai->ai_addrlen))
+    return 1;
   if (numsock >= MAXSOCK)
     error(0, "too many listening sockets (%d max)", MAXSOCK);
   fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
diff --git a/rbldnsd.py b/rbldnsd.py
index 9300ef2..4b78dee 100644
--- a/rbldnsd.py
+++ b/rbldnsd.py
@@ -2,6 +2,7 @@
 
 
 """
+import errno
 from itertools import count
 import subprocess
 from tempfile import NamedTemporaryFile, TemporaryFile
@@ -12,6 +13,14 @@ try:
     import DNS
 except ImportError:
     raise RuntimeError("The pydns library is not installed")
+try:
+    from DNS import SocketError as DNS_SocketError
+except ImportError:
+    class DNS_SocketError(Exception):
+        """ Dummy, never raised.
+
+        (Older versions of pydns before 2.3.6 do not raise SocketError.)
+        """
 
 DUMMY_ZONE_HEADER = """
 $SOA 0 example.org. hostmaster.example.com. 0 1h 1h 2d 1h
@@ -113,7 +122,6 @@ class Rbldnsd(object):
                                                  stderr=self.stderr)
 
         # wait for rbldnsd to start responding
-        time.sleep(0.1)
         for retry in count():
             if daemon.poll() is not None:
                 raise DaemonError(
@@ -124,12 +132,18 @@ class Rbldnsd(object):
                 break
             except QueryRefused:
                 break
+            except DNS_SocketError as ex:
+                # pydns >= 2.3.6
+                wrapped_error = ex.args[0]
+                if wrapped_error.errno != errno.ECONNREFUSED:
+                    raise
             except DNS.DNSError as ex:
+                # pydns < 2.3.6
                 if str(ex) != 'no working nameservers found':
                     raise
-                elif retries > 10:
-                    raise DaemonError(
-                        "rbldnsd does not seem to be responding")
+            if retry > 10:
+                raise DaemonError("rbldnsd does not seem to be responding")
+            time.sleep(0.1)
 
     def _stop_daemon(self):
         daemon = self._daemon
@@ -150,6 +164,22 @@ class Rbldnsd(object):
             raise DaemonError("rbldnsd exited with code %d"
                               % daemon.returncode)
 
+    @property
+    def no_ipv6(self):
+        """ Was rbldnsd compiled with -DNO_IPv6?
+        """
+        # If rbldnsd was compiled with -DNO_IPv6, the (therefore
+        # unsupported) '-6' command-line switch will not be described
+        # in the help message
+        cmd = [self.daemon_bin, '-h']
+        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+        help_message = proc.stdout.readlines()
+        if proc.wait() != 0:
+            raise subprocess.CalledProcessError(proc.returncode, cmd)
+        return not any(line.lstrip().startswith('-6 ')
+                       for line in help_message)
+
+
 class TestRbldnsd(unittest.TestCase):
     def test(self):
         rbldnsd = Rbldnsd()
diff --git a/test_acl.py b/test_acl.py
index d93ca0a..10bed1c 100644
--- a/test_acl.py
+++ b/test_acl.py
@@ -1,5 +1,8 @@
 """ Tests for the acl dataset
 """
+from functools import wraps
+import socket
+import sys
 from tempfile import NamedTemporaryFile
 import unittest
 
@@ -9,6 +12,35 @@ __all__ = [
     'TestAclDataset',
     ]
 
+try:
+    from unittest import skipIf
+except ImportError:
+    # hokey replacement (for python <= 2.6)
+    def skipIf(condition, reason):
+        if condition:
+            def decorate(f):
+                @wraps(f)
+                def skipped(*args, **kw):
+                    sys.stderr.write("skipped test: %s " % reason)
+                return skipped
+            return decorate
+        else:
+            return lambda f: f
+
+def _have_ipv6():
+    # Check for IPv6 support
+    if not getattr(socket, 'has_ipv6', False):
+        return False                    # no python support for ipv6
+    elif Rbldnsd().no_ipv6:
+        return False                    # rbldnsd compiled with -DNO_IPv6
+    try:
+        socket.socket(socket.AF_INET6, socket.SOCK_DGRAM).close()
+    except socket.error:
+        return False                    # no kernel (or libc) support for ipv6?
+    return True
+
+no_ipv6 = not _have_ipv6()
+
 def daemon(acl, addr='localhost'):
     """ Create an Rbldnsd instance with given ACL
     """
@@ -33,11 +65,13 @@ class TestAclDataset(unittest.TestCase):
                     addr='127.0.0.1') as dnsd:
             self.assertEqual(dnsd.query('test.example.com'), 'Success')
 
+    @skipIf(no_ipv6, "IPv6 unsupported")
     def test_refuse_ipv6(self):
         with daemon(acl=["::1 :refuse"],
                     addr='::1') as dnsd:
             self.assertRaises(QueryRefused, dnsd.query, 'test.example.com')
 
+    @skipIf(no_ipv6, "IPv6 unsupported")
     def test_pass_ipv6(self):
         with daemon(acl=[ "0/0 :refuse",
                           "0::1 :pass" ],
diff --git a/test_ip4trie.py b/test_ip4trie.py
index fe9e78f..2cce09b 100644
--- a/test_ip4trie.py
+++ b/test_ip4trie.py
@@ -9,7 +9,7 @@ __all__ = [
     ]
 
 def ip4trie(zone_data):
-    """ Run rbldnsd with an ip6trie dataset
+    """ Run rbldnsd with an ip4trie dataset
     """
     dnsd = Rbldnsd()
     dnsd.add_dataset('ip4trie', ZoneFile(zone_data))
diff --git a/test_ip6trie.py b/test_ip6trie.py
index d3600db..377c5dd 100644
--- a/test_ip6trie.py
+++ b/test_ip6trie.py
@@ -15,15 +15,6 @@ def ip6trie(zone_data):
     dnsd.add_dataset('ip6trie', ZoneFile(zone_data))
     return dnsd
 
-def rfc3152(ip6addr, domain='example.com'):
-    from socket import inet_pton, AF_INET6
-    from struct import unpack
-
-    bytes = unpack("16B", inet_pton(AF_INET6, ip6addr))
-    nibbles = '.'.join("%x.%x" % (byte & 0xf, (byte >> 4) & 0xf)
-                       for byte in reversed(bytes))
-    return "%s.%s" % (nibbles, domain)
-
 class TestIp6TrieDataset(unittest.TestCase):
     def test_exclusion(self):
         with ip6trie(["dead::/16 listed",
@@ -31,5 +22,35 @@ class TestIp6TrieDataset(unittest.TestCase):
             self.assertEqual(dnsd.query(rfc3152("dead::beef")), None)
             self.assertEqual(dnsd.query(rfc3152("dead::beee")), "listed")
 
+
+def rfc3152(ip6addr, domain='example.com'):
+    return "%s.%s" % ('.'.join(reversed(_to_nibbles(ip6addr))), domain)
+
+def _to_nibbles(ip6addr):
+    """ Convert ip6 address (in rfc4291 notation) to a sequence of nibbles
+
+    NB: We avoid the use of socket.inet_pton(AF_INET6, ip6addr) here
+    because it fails (with 'error: can't use AF_INET6, IPv6 is
+    disabled') when python has been compiled without IPv6 support. See
+    http://www.corpit.ru/pipermail/rbldnsd/2013q3/001181.html
+
+    """
+    def _split_words(addr):
+        return [ int(w, 16) for w in addr.split(':') ] if addr else []
+
+    if '::' in ip6addr:
+        head, tail = [ _split_words(s) for s in ip6addr.split('::', 1) ]
+        nzeros = 8 - len(head) - len(tail)
+        assert nzeros >= 0
+        words = head + [ 0 ] * nzeros + tail
+    else:
+        words = _split_words(ip6addr)
+
+    assert len(words) == 8
+    for word in words:
+        assert 0 <= word <= 0xffff
+
+    return ''.join("%04x" % word for word in words)
+
 if __name__ == '__main__':
     unittest.main()