mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-18 18:56:28 +00:00
import/delegate-to foolscap's allocate_tcp_port
(instead of using a copy). Foolscap-0.12.3 fixes a problem with allocate_tcp_port() that was causing intermittent test failures. I think it makes more sense to use Foolscap's copy (and fixes) than to keep re-copying it into Tahoe each time it changes. If/when we manage to stop depending upon foolscap for server RPC, we can re-copy this back into tahoe's source tree. refs ticket:2795
This commit is contained in:
parent
076b3895dc
commit
57e7f7bb7c
@ -39,7 +39,8 @@ install_requires = [
|
||||
# * foolscap 0.8.0 generates 2048-bit RSA-with-SHA-256 signatures,
|
||||
# rather than 1024-bit RSA-with-MD5. This also allows us to work
|
||||
# with a FIPS build of OpenSSL.
|
||||
# * foolscap >= 0.12.3 provides tcp/tor/i2p connection handlers we need
|
||||
# * foolscap >= 0.12.3 provides tcp/tor/i2p connection handlers we need,
|
||||
# and allocate_tcp_port
|
||||
"foolscap >= 0.12.3",
|
||||
|
||||
# Needed for SFTP.
|
||||
|
@ -1,5 +1,5 @@
|
||||
# from the Python Standard Library
|
||||
import os, sys, re, socket, subprocess, errno
|
||||
import os, re, socket, subprocess, errno
|
||||
|
||||
from sys import platform
|
||||
|
||||
@ -8,9 +8,10 @@ from twisted.internet import defer, threads, reactor
|
||||
from twisted.internet.protocol import DatagramProtocol
|
||||
from twisted.internet.error import CannotListenError
|
||||
from twisted.python.procutils import which
|
||||
from twisted.python.runtime import platformType
|
||||
from twisted.python import log
|
||||
|
||||
from foolscap.util import allocate_tcp_port # re-exported
|
||||
|
||||
try:
|
||||
import resource
|
||||
def increase_rlimits():
|
||||
@ -237,70 +238,9 @@ def _cygwin_hack_find_addresses():
|
||||
|
||||
return defer.succeed(addresses)
|
||||
|
||||
def allocate_tcp_port():
|
||||
"""Return an (integer) available TCP port on localhost. This briefly
|
||||
listens on the port in question, then closes it right away."""
|
||||
|
||||
# Making this work correctly on multiple OSes is non-trivial:
|
||||
# * on OS-X:
|
||||
# * Binding the test socket to 127.0.0.1 lets the kernel give us a
|
||||
# LISTEN port that some other process is using, if they bound it to
|
||||
# ANY (0.0.0.0). These will fail when we attempt to
|
||||
# listen(bind=0.0.0.0) ourselves
|
||||
# * Binding the test socket to 0.0.0.0 lets the kernel give us LISTEN
|
||||
# ports bound to 127.0.0.1, although then our subsequent listen()
|
||||
# call usually succeeds.
|
||||
# * In both cases, the kernel can give us a port that's in use by the
|
||||
# near side of an ESTABLISHED socket. If the process which owns that
|
||||
# socket is not owned by the same user as us, listen() will fail.
|
||||
# * Doing a listen() right away (on the kernel-allocated socket)
|
||||
# succeeds, but a subsequent listen() on a new socket (bound to
|
||||
# the same port) will fail.
|
||||
# * on Linux:
|
||||
# * The kernel never gives us a port in use by a LISTEN socket, whether
|
||||
# we bind the test socket to 127.0.0.1 or 0.0.0.0
|
||||
# * Binding it to 127.0.0.1 does let the kernel give us ports used in
|
||||
# an ESTABLISHED connection. Our listen() will fail regardless of who
|
||||
# owns that socket. (note that we are using SO_REUSEADDR but not
|
||||
# SO_REUSEPORT, which would probably affect things).
|
||||
#
|
||||
# So to make this work properly everywhere, allocate_tcp_port() needs two
|
||||
# phases: first we allocate a port (with 0.0.0.0), then we close that
|
||||
# socket, then we open a second socket, bind the second socket to the
|
||||
# same port, then try to listen. If the listen() fails, we loop back and
|
||||
# try again.
|
||||
|
||||
# Ideally we'd refrain from doing listen(), to minimize impact on the
|
||||
# system, and we'd bind the port to 127.0.0.1, to avoid making it look
|
||||
# like we're accepting data from the outside world (in situations where
|
||||
# we're going to end up binding the port to 127.0.0.1 anyways). But for
|
||||
# the above reasons, neither would work. We *do* add SO_REUSEADDR, to
|
||||
# make sure our lingering socket won't prevent our caller from opening it
|
||||
# themselves in a few moments (note that Twisted's
|
||||
# tcp.Port.createInternetSocket sets SO_REUSEADDR, among other flags).
|
||||
|
||||
count = 0
|
||||
while True:
|
||||
s = _make_socket()
|
||||
s.bind(("0.0.0.0", 0))
|
||||
port = s.getsockname()[1]
|
||||
s.close()
|
||||
|
||||
s = _make_socket()
|
||||
try:
|
||||
s.bind(("0.0.0.0", port))
|
||||
s.listen(5) # this is what sometimes fails
|
||||
s.close()
|
||||
return port
|
||||
except socket.error:
|
||||
s.close()
|
||||
count += 1
|
||||
if count > 100:
|
||||
raise
|
||||
# try again
|
||||
|
||||
def _make_socket():
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
if platformType == "posix" and sys.platform != "cygwin":
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
return s
|
||||
__all__ = ["allocate_tcp_port",
|
||||
"increase_rlimits",
|
||||
"get_local_addresses_sync",
|
||||
"get_local_addresses_async",
|
||||
"get_local_ip_for",
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user