]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Sort getaddrinfo result based on address family 2454/head
authorSilent-Nights <silentquote@gmail.com>
Mon, 23 Jul 2018 13:42:44 +0000 (15:42 +0200)
committerSilent-Nights <silentquote@gmail.com>
Mon, 23 Jul 2018 14:03:04 +0000 (16:03 +0200)
Putting getaddrinfo result in a set to eliminate duplicates causes race
 conditions because sets are unordered

For example consider 2 applications try to bind to many random ports at once
 in both IPV4 and IPV6.
App1: Bind to random IPV4 address and get port 50000
App2: Bind to random IPV6 address and get port 50000
App2: Try to bind to same port on IPV4 and fails as App1 already bound to it
The same goes for App1

To resolve this, we create the set explicitly outside the loop.
We order the returned list of getaddrinfo based on Address family.
We skip the iteration if the iteration tuple already in the set.
Otherwise we execute the loop.

tornado/netutil.py

index c57f6fd34787fd826594dcc78be963c432fbcae4..7bb587dce70f476a7934703c9c432934be0b2e77 100644 (file)
@@ -102,8 +102,14 @@ def bind_sockets(port, address=None, family=socket.AF_UNSPEC,
     if flags is None:
         flags = socket.AI_PASSIVE
     bound_port = None
-    for res in set(socket.getaddrinfo(address, port, family, socket.SOCK_STREAM,
-                                      0, flags)):
+    unique_addresses = set()
+    for res in sorted(socket.getaddrinfo(address, port, family, socket.SOCK_STREAM,
+                                         0, flags), key=lambda x: x[0]):
+        if res in unique_addresses:
+            continue
+
+        unique_addresses.add(res)
+
         af, socktype, proto, canonname, sockaddr = res
         if (sys.platform == 'darwin' and address == 'localhost' and
                 af == socket.AF_INET6 and sockaddr[3] != 0):