2020-07-27 11:46:03 -04:00
|
|
|
"""
|
|
|
|
Utilities for getting IP addresses.
|
|
|
|
|
|
|
|
Ported to Python 3.
|
|
|
|
"""
|
|
|
|
|
2020-07-27 11:44:58 -04:00
|
|
|
from __future__ import absolute_import
|
|
|
|
from __future__ import division
|
|
|
|
from __future__ import print_function
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
from future.utils import PY2, native_str
|
|
|
|
if PY2:
|
2020-08-05 11:53:23 -04:00
|
|
|
from builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401
|
2020-07-27 11:44:58 -04:00
|
|
|
|
2016-09-01 22:39:03 -07:00
|
|
|
import os, re, socket, subprocess, errno
|
2013-06-26 16:44:05 +01:00
|
|
|
from sys import platform
|
2006-11-30 16:23:39 -07:00
|
|
|
|
2019-05-01 13:30:14 -04:00
|
|
|
from zope.interface import implementer
|
|
|
|
|
|
|
|
import attr
|
|
|
|
|
2007-04-16 14:59:13 -07:00
|
|
|
# from Twisted
|
2019-04-16 11:26:15 -04:00
|
|
|
from twisted.python.reflect import requireModule
|
2011-01-19 20:32:38 -08:00
|
|
|
from twisted.internet import defer, threads, reactor
|
2006-11-30 16:23:39 -07:00
|
|
|
from twisted.internet.protocol import DatagramProtocol
|
2014-12-29 20:21:24 -05:00
|
|
|
from twisted.internet.error import CannotListenError
|
2007-04-16 14:59:13 -07:00
|
|
|
from twisted.python.procutils import which
|
2007-12-07 08:03:43 -07:00
|
|
|
from twisted.python import log
|
2019-04-16 11:26:15 -04:00
|
|
|
from twisted.internet.endpoints import AdoptedStreamServerEndpoint
|
2019-05-01 13:30:14 -04:00
|
|
|
from twisted.internet.interfaces import (
|
|
|
|
IReactorSocket,
|
|
|
|
IStreamServerEndpoint,
|
|
|
|
)
|
2019-04-16 11:26:15 -04:00
|
|
|
|
2019-05-01 15:47:44 -04:00
|
|
|
from .gcutil import (
|
|
|
|
fileDescriptorResource,
|
|
|
|
)
|
|
|
|
|
2019-04-16 11:26:15 -04:00
|
|
|
fcntl = requireModule("fcntl")
|
2007-03-07 18:22:30 -07:00
|
|
|
|
2016-09-01 22:39:03 -07:00
|
|
|
from foolscap.util import allocate_tcp_port # re-exported
|
|
|
|
|
2007-12-07 08:03:43 -07:00
|
|
|
try:
|
|
|
|
import resource
|
|
|
|
def increase_rlimits():
|
|
|
|
# We'd like to raise our soft resource.RLIMIT_NOFILE, since certain
|
|
|
|
# systems (OS-X, probably solaris) start with a relatively low limit
|
|
|
|
# (256), and some unit tests want to open up more sockets than this.
|
|
|
|
# Most linux systems start with both hard and soft limits at 1024,
|
|
|
|
# which is plenty.
|
|
|
|
|
|
|
|
# unfortunately the values to pass to setrlimit() vary widely from
|
|
|
|
# one system to another. OS-X reports (256, HUGE), but the real hard
|
|
|
|
# limit is 10240, and accepts (-1,-1) to mean raise it to the
|
|
|
|
# maximum. Cygwin reports (256, -1), then ignores a request of
|
|
|
|
# (-1,-1): instead you have to guess at the hard limit (it appears to
|
|
|
|
# be 3200), so using (3200,-1) seems to work. Linux reports a
|
|
|
|
# sensible (1024,1024), then rejects (-1,-1) as trying to raise the
|
|
|
|
# maximum limit, so you could set it to (1024,1024) but you might as
|
|
|
|
# well leave it alone.
|
|
|
|
|
|
|
|
try:
|
|
|
|
current = resource.getrlimit(resource.RLIMIT_NOFILE)
|
|
|
|
except AttributeError:
|
2007-12-12 19:34:08 -07:00
|
|
|
# we're probably missing RLIMIT_NOFILE
|
2007-12-07 08:03:43 -07:00
|
|
|
return
|
|
|
|
|
|
|
|
if current[0] >= 1024:
|
|
|
|
# good enough, leave it alone
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
|
|
|
if current[1] > 0 and current[1] < 1000000:
|
|
|
|
# solaris reports (256, 65536)
|
|
|
|
resource.setrlimit(resource.RLIMIT_NOFILE,
|
|
|
|
(current[1], current[1]))
|
|
|
|
else:
|
|
|
|
# this one works on OS-X (bsd), and gives us 10240, but
|
|
|
|
# it doesn't work on linux (on which both the hard and
|
|
|
|
# soft limits are set to 1024 by default).
|
|
|
|
resource.setrlimit(resource.RLIMIT_NOFILE, (-1,-1))
|
|
|
|
new = resource.getrlimit(resource.RLIMIT_NOFILE)
|
|
|
|
if new[0] == current[0]:
|
|
|
|
# probably cygwin, which ignores -1. Use a real value.
|
|
|
|
resource.setrlimit(resource.RLIMIT_NOFILE, (3200,-1))
|
|
|
|
|
|
|
|
except ValueError:
|
|
|
|
log.msg("unable to set RLIMIT_NOFILE: current value %s"
|
|
|
|
% (resource.getrlimit(resource.RLIMIT_NOFILE),))
|
|
|
|
except:
|
|
|
|
# who knows what. It isn't very important, so log it and continue
|
|
|
|
log.err()
|
|
|
|
except ImportError:
|
2008-01-14 18:27:43 -07:00
|
|
|
def _increase_rlimits():
|
2007-12-07 08:03:43 -07:00
|
|
|
# TODO: implement this for Windows. Although I suspect the
|
|
|
|
# solution might be "be running under the iocp reactor and
|
|
|
|
# make this function be a no-op".
|
|
|
|
pass
|
2008-01-14 18:27:43 -07:00
|
|
|
# pyflakes complains about two 'def FOO' statements in the same time,
|
|
|
|
# since one might be shadowing the other. This hack appeases pyflakes.
|
|
|
|
increase_rlimits = _increase_rlimits
|
|
|
|
|
2016-04-26 18:18:13 -07:00
|
|
|
def get_local_addresses_sync():
|
2020-07-28 11:11:05 -04:00
|
|
|
"""
|
|
|
|
Return a list of IPv4 addresses (as dotted-quad native strings) that are
|
|
|
|
currently configured on this host, sorted in descending order of how likely
|
|
|
|
we think they are to work.
|
|
|
|
"""
|
2020-07-27 15:20:35 -04:00
|
|
|
return [native_str(a) for a in _synchronously_find_addresses_via_config()]
|
2007-12-07 08:03:43 -07:00
|
|
|
|
2008-10-28 13:36:46 -07:00
|
|
|
def get_local_addresses_async(target="198.41.0.4"): # A.ROOT-SERVERS.NET
|
2007-04-16 14:59:13 -07:00
|
|
|
"""
|
|
|
|
Return a Deferred that fires with a list of IPv4 addresses (as dotted-quad
|
2020-07-28 11:11:05 -04:00
|
|
|
native strings) that are currently configured on this host, sorted in
|
|
|
|
descending order of how likely we think they are to work.
|
2007-03-07 20:03:47 -07:00
|
|
|
|
2007-04-16 14:59:13 -07:00
|
|
|
@param target: we want to learn an IP address they could try using to
|
|
|
|
connect to us; The default value is fine, but it might help if you
|
|
|
|
pass the address of a host that you are actually trying to be
|
|
|
|
reachable to.
|
2007-03-07 18:22:30 -07:00
|
|
|
"""
|
2007-05-22 14:06:37 -07:00
|
|
|
addresses = []
|
2007-06-07 19:23:33 -07:00
|
|
|
local_ip = get_local_ip_for(target)
|
2013-06-25 19:51:27 +01:00
|
|
|
if local_ip is not None:
|
2007-06-07 19:23:33 -07:00
|
|
|
addresses.append(local_ip)
|
2007-04-18 17:30:08 -07:00
|
|
|
|
2013-06-26 16:44:05 +01:00
|
|
|
if platform == "cygwin":
|
2013-06-25 19:51:27 +01:00
|
|
|
d = _cygwin_hack_find_addresses()
|
2007-05-22 14:06:37 -07:00
|
|
|
else:
|
|
|
|
d = _find_addresses_via_config()
|
2007-03-07 18:43:17 -07:00
|
|
|
|
2007-04-16 14:59:13 -07:00
|
|
|
def _collect(res):
|
2007-05-22 14:06:37 -07:00
|
|
|
for addr in res:
|
2007-10-13 00:38:16 -07:00
|
|
|
if addr != "0.0.0.0" and not addr in addresses:
|
2007-05-22 14:06:37 -07:00
|
|
|
addresses.append(addr)
|
2007-03-07 18:22:30 -07:00
|
|
|
return addresses
|
2007-04-16 14:59:13 -07:00
|
|
|
d.addCallback(_collect)
|
2020-07-27 16:01:12 -04:00
|
|
|
d.addCallback(lambda addresses: [native_str(s) for s in addresses])
|
2007-04-16 14:59:13 -07:00
|
|
|
return d
|
2006-11-30 16:23:39 -07:00
|
|
|
|
2007-04-16 14:59:13 -07:00
|
|
|
def get_local_ip_for(target):
|
2006-11-30 16:23:39 -07:00
|
|
|
"""Find out what our IP address is for use by a given target.
|
|
|
|
|
2020-07-28 11:11:05 -04:00
|
|
|
@return: the IP address as a dotted-quad native string which could be used
|
2007-06-07 19:23:33 -07:00
|
|
|
to connect to us. It might work for them, it might not. If
|
|
|
|
there is no suitable address (perhaps we don't currently have an
|
|
|
|
externally-visible interface), this will return None.
|
2006-11-30 16:23:39 -07:00
|
|
|
"""
|
2007-06-07 19:23:33 -07:00
|
|
|
|
|
|
|
try:
|
|
|
|
target_ipaddr = socket.gethostbyname(target)
|
|
|
|
except socket.gaierror:
|
2008-10-28 13:36:46 -07:00
|
|
|
# DNS isn't running, or somehow we encountered an error
|
|
|
|
|
|
|
|
# note: if an interface is configured and up, but nothing is
|
|
|
|
# connected to it, gethostbyname("A.ROOT-SERVERS.NET") will take 20
|
|
|
|
# seconds to raise socket.gaierror . This is synchronous and occurs
|
|
|
|
# for each node being started, so users of
|
|
|
|
# test.common.SystemTestMixin (like test_system) will see something
|
|
|
|
# like 120s of delay, which may be enough to hit the default trial
|
|
|
|
# timeouts. For that reason, get_local_addresses_async() was changed
|
|
|
|
# to default to the numerical ip address for A.ROOT-SERVERS.NET, to
|
|
|
|
# avoid this DNS lookup. This also makes node startup fractionally
|
|
|
|
# faster.
|
2007-06-07 19:23:33 -07:00
|
|
|
return None
|
2015-02-04 01:35:44 -08:00
|
|
|
|
2007-06-07 19:23:33 -07:00
|
|
|
try:
|
2014-12-29 20:21:24 -05:00
|
|
|
udpprot = DatagramProtocol()
|
|
|
|
port = reactor.listenUDP(0, udpprot)
|
2016-04-03 01:01:08 -07:00
|
|
|
try:
|
|
|
|
# connect() will fail if we're offline (e.g. running tests on a
|
|
|
|
# disconnected laptop), which is fine (localip=None), but we must
|
|
|
|
# still do port.stopListening() or we'll get a DirtyReactorError
|
|
|
|
udpprot.transport.connect(target_ipaddr, 7)
|
|
|
|
localip = udpprot.transport.getHost().host
|
|
|
|
return localip
|
|
|
|
finally:
|
|
|
|
d = port.stopListening()
|
2020-07-28 11:11:27 -04:00
|
|
|
d.addErrback(log.err)
|
2014-12-29 20:21:24 -05:00
|
|
|
except (socket.error, CannotListenError):
|
2007-06-07 19:23:33 -07:00
|
|
|
# no route to that host
|
|
|
|
localip = None
|
2020-07-27 15:20:35 -04:00
|
|
|
return native_str(localip)
|
2006-12-02 19:37:31 -07:00
|
|
|
|
2007-04-16 14:59:13 -07:00
|
|
|
|
|
|
|
# Wow, I'm really amazed at home much mileage we've gotten out of calling
|
|
|
|
# the external route.exe program on windows... It appears to work on all
|
2015-02-19 17:41:09 +00:00
|
|
|
# versions so far.
|
2007-04-16 14:59:13 -07:00
|
|
|
# ... thus wrote Greg Smith in time immemorial...
|
2015-02-19 17:41:09 +00:00
|
|
|
# Also, the Win32 APIs for this are really klunky and error-prone. --Daira
|
|
|
|
|
2020-07-27 11:42:20 -04:00
|
|
|
_win32_re = re.compile(br'^\s*\d+\.\d+\.\d+\.\d+\s.+\s(?P<address>\d+\.\d+\.\d+\.\d+)\s+(?P<metric>\d+)\s*$', flags=re.M|re.I|re.S)
|
2013-05-28 19:52:19 +01:00
|
|
|
_win32_commands = (('route.exe', ('print',), _win32_re),)
|
2007-04-16 14:59:13 -07:00
|
|
|
|
2013-05-28 19:52:19 +01:00
|
|
|
# These work in most Unices.
|
2020-07-27 11:42:20 -04:00
|
|
|
_addr_re = re.compile(br'^\s*inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)[\s/].+$', flags=re.M|re.I|re.S)
|
2013-06-25 19:13:48 +01:00
|
|
|
_unix_commands = (('/bin/ip', ('addr',), _addr_re),
|
2016-10-12 11:00:05 +02:00
|
|
|
('/sbin/ip', ('addr',), _addr_re),
|
2013-05-28 19:52:19 +01:00
|
|
|
('/sbin/ifconfig', ('-a',), _addr_re),
|
|
|
|
('/usr/sbin/ifconfig', ('-a',), _addr_re),
|
|
|
|
('/usr/etc/ifconfig', ('-a',), _addr_re),
|
|
|
|
('ifconfig', ('-a',), _addr_re),
|
|
|
|
('/sbin/ifconfig', (), _addr_re),
|
|
|
|
)
|
2007-04-16 14:59:13 -07:00
|
|
|
|
2011-01-19 20:32:38 -08:00
|
|
|
|
2007-04-16 14:59:13 -07:00
|
|
|
def _find_addresses_via_config():
|
2011-01-19 20:32:38 -08:00
|
|
|
return threads.deferToThread(_synchronously_find_addresses_via_config)
|
|
|
|
|
|
|
|
def _synchronously_find_addresses_via_config():
|
2013-05-28 19:52:19 +01:00
|
|
|
# originally by Greg Smith, hacked by Zooko and then Daira
|
2007-11-01 15:25:16 -07:00
|
|
|
|
2013-05-28 19:52:19 +01:00
|
|
|
# We don't reach here for cygwin.
|
2013-06-26 16:44:05 +01:00
|
|
|
if platform == 'win32':
|
2013-05-28 19:52:19 +01:00
|
|
|
commands = _win32_commands
|
|
|
|
else:
|
|
|
|
commands = _unix_commands
|
2007-04-16 14:59:13 -07:00
|
|
|
|
2013-05-28 19:52:19 +01:00
|
|
|
for (pathtotool, args, regex) in commands:
|
|
|
|
# If pathtotool is a fully qualified path then we just try that.
|
|
|
|
# If it is merely an executable name then we use Twisted's
|
|
|
|
# "which()" utility and try each executable in turn until one
|
|
|
|
# gives us something that resembles a dotted-quad IPv4 address.
|
2007-04-18 17:30:08 -07:00
|
|
|
|
2013-05-28 19:52:19 +01:00
|
|
|
if os.path.isabs(pathtotool):
|
|
|
|
exes_to_try = [pathtotool]
|
|
|
|
else:
|
|
|
|
exes_to_try = which(pathtotool)
|
2007-08-09 10:56:47 -07:00
|
|
|
|
2020-07-27 11:42:20 -04:00
|
|
|
subprocess_error = getattr(
|
|
|
|
subprocess, "SubprocessError", subprocess.CalledProcessError
|
|
|
|
)
|
2011-01-19 20:32:38 -08:00
|
|
|
for exe in exes_to_try:
|
|
|
|
try:
|
|
|
|
addresses = _query(exe, args, regex)
|
2020-07-27 11:42:20 -04:00
|
|
|
except (IOError, OSError, ValueError, subprocess_error):
|
2011-01-19 20:32:38 -08:00
|
|
|
addresses = []
|
|
|
|
if addresses:
|
|
|
|
return addresses
|
2013-05-28 19:52:19 +01:00
|
|
|
|
|
|
|
return []
|
2007-11-01 15:25:16 -07:00
|
|
|
|
2007-04-18 17:30:08 -07:00
|
|
|
def _query(path, args, regex):
|
Avoid Popen() of executables that don't exist
The stdlib 'subprocess' module in python-2.7.4 through 2.7.7 suffers
from http://bugs.python.org/issue18851 which causes unrelated file
descriptors to be closed when `subprocess.call()` fails the `exec()`,
such as when the executable being invoked does not actually exist. There
appears to be some randomness involved. This was fixed in python-2.7.8.
Tahoe's iputil.py uses subprocess.call on many different "ifconfig"-type
executables, most of which don't exist on any given platform (added in
git commit 8e31d66cd0b). This results in a lot of file-descriptor
closing, which (at least during unit tests) tends to clobber important
things like Tub TCP sockets. This seems to be the root cause behind
ticket:2121, in which normal code tries to close already-closed sockets,
crashing the unit tests. Since different platforms have different
ifconfigs, some platforms will experience more failed execs than others,
so this bug could easily behave differently on linux vs freebsd, as well
as working normally on python-2.7.8 or 2.7.4.
This patch inserts a guard to make sure that os.path.isfile() is true
before allowing Popen.call() to try executing the target. This ought to
be enough to avoid the bug. It changes both iputil.py and
allmydata.__init__ (which uses Popen for calling "lsb_release"), which
are all the places where 'subprocess' is used outside of unit tests.
Other potential fixes: use the 'subprocess32' module from PyPI (which is
a bug-free backport of the Python3 stdlib subprocess module, but would
introduce a new dependency), or require python >= 2.7.8 (but this would
rule out development/deployment on the current OS-X 10.9 release, which
ships with 2.7.5, as well as other distributions like Ubuntu 14.04 LTS).
I believe this closes ticket:2121, and given the apparent relationship
between 2121 and 2023, I think it also closes ticket:2023 (although
since 2023 doesn't have copies of the failing log files, it's hard to
tell). I'm hoping that this will tide us over until 1.11 is released, at
which point we can execute on the plan to remove iputil.py entirely by
changing the way that nodes learn their externally-facing IP address.
2014-09-11 13:13:42 -07:00
|
|
|
if not os.path.isfile(path):
|
|
|
|
return []
|
2020-07-27 16:19:36 -04:00
|
|
|
env = {native_str('LANG'): native_str('en_US.UTF-8')}
|
2013-06-25 19:13:48 +01:00
|
|
|
TRIES = 5
|
2020-07-27 11:42:20 -04:00
|
|
|
for trial in range(TRIES):
|
2013-05-28 19:44:02 +01:00
|
|
|
try:
|
|
|
|
p = subprocess.Popen([path] + list(args), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
|
|
|
|
(output, err) = p.communicate()
|
|
|
|
break
|
2019-03-28 12:45:28 +01:00
|
|
|
except OSError as e:
|
2013-06-25 19:13:48 +01:00
|
|
|
if e.errno == errno.EINTR and trial < TRIES-1:
|
2013-05-28 19:44:02 +01:00
|
|
|
continue
|
2013-06-25 19:13:48 +01:00
|
|
|
raise
|
2007-11-01 15:25:16 -07:00
|
|
|
|
2011-01-19 20:32:38 -08:00
|
|
|
addresses = []
|
2020-07-27 11:42:20 -04:00
|
|
|
outputsplit = output.split(b'\n')
|
2011-01-19 20:32:38 -08:00
|
|
|
for outline in outputsplit:
|
|
|
|
m = regex.match(outline)
|
|
|
|
if m:
|
2013-06-26 16:44:05 +01:00
|
|
|
addr = m.group('address')
|
2011-01-19 20:32:38 -08:00
|
|
|
if addr not in addresses:
|
2020-07-27 11:42:20 -04:00
|
|
|
addresses.append(addr.decode("utf-8"))
|
2011-01-19 20:32:38 -08:00
|
|
|
|
|
|
|
return addresses
|
2007-04-18 17:30:08 -07:00
|
|
|
|
2013-06-25 19:51:27 +01:00
|
|
|
def _cygwin_hack_find_addresses():
|
2007-05-22 14:06:37 -07:00
|
|
|
addresses = []
|
2013-06-25 19:51:27 +01:00
|
|
|
for h in ["localhost", "127.0.0.1",]:
|
|
|
|
addr = get_local_ip_for(h)
|
|
|
|
if addr is not None and addr not in addresses:
|
|
|
|
addresses.append(addr)
|
2007-04-18 17:30:08 -07:00
|
|
|
|
2007-05-22 14:06:37 -07:00
|
|
|
return defer.succeed(addresses)
|
2016-04-26 13:09:56 -07:00
|
|
|
|
2019-04-16 11:26:15 -04:00
|
|
|
|
|
|
|
def _foolscapEndpointForPortNumber(portnum):
|
|
|
|
"""
|
|
|
|
Create an endpoint that can be passed to ``Tub.listen``.
|
|
|
|
|
|
|
|
:param portnum: Either an integer port number indicating which TCP/IPv4
|
|
|
|
port number the endpoint should bind or ``None`` to automatically
|
|
|
|
allocate such a port number.
|
|
|
|
|
|
|
|
:return: A two-tuple of the integer port number allocated and a
|
|
|
|
Foolscap-compatible endpoint object.
|
|
|
|
"""
|
|
|
|
if portnum is None:
|
|
|
|
# Bury this reactor import here to minimize the chances of it having
|
|
|
|
# the effect of installing the default reactor.
|
|
|
|
from twisted.internet import reactor
|
|
|
|
if fcntl is not None and IReactorSocket.providedBy(reactor):
|
|
|
|
# On POSIX we can take this very safe approach of binding the
|
|
|
|
# actual socket to an address. Once the bind succeeds here, we're
|
|
|
|
# no longer subject to any future EADDRINUSE problems.
|
|
|
|
s = socket.socket()
|
|
|
|
try:
|
|
|
|
s.bind(('', 0))
|
|
|
|
portnum = s.getsockname()[1]
|
|
|
|
s.listen(1)
|
2019-05-01 15:47:44 -04:00
|
|
|
# File descriptors are a relatively scarce resource. The
|
|
|
|
# cleanup process for the file descriptor we're about to dup
|
|
|
|
# is unfortunately complicated. In particular, it involves
|
|
|
|
# the Python garbage collector. See CleanupEndpoint for
|
|
|
|
# details of that. Here, we need to make sure the garbage
|
|
|
|
# collector actually runs frequently enough to make a
|
|
|
|
# difference. Normally, the garbage collector is triggered by
|
|
|
|
# allocations. It doesn't know about *file descriptor*
|
|
|
|
# allocation though. So ... we'll "teach" it about those,
|
|
|
|
# here.
|
|
|
|
fileDescriptorResource.allocate()
|
2019-04-16 11:26:15 -04:00
|
|
|
fd = os.dup(s.fileno())
|
|
|
|
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
|
|
|
flags = flags | os.O_NONBLOCK | fcntl.FD_CLOEXEC
|
|
|
|
fcntl.fcntl(fd, fcntl.F_SETFD, flags)
|
2019-05-01 13:30:14 -04:00
|
|
|
endpoint = AdoptedStreamServerEndpoint(reactor, fd, socket.AF_INET)
|
|
|
|
return (portnum, CleanupEndpoint(endpoint, fd))
|
2019-04-16 11:26:15 -04:00
|
|
|
finally:
|
|
|
|
s.close()
|
|
|
|
else:
|
|
|
|
# Get a random port number and fall through. This is necessary on
|
|
|
|
# Windows where Twisted doesn't offer IReactorSocket. This
|
|
|
|
# approach is error prone for the reasons described on
|
|
|
|
# https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2787
|
|
|
|
portnum = allocate_tcp_port()
|
2020-07-27 15:16:34 -04:00
|
|
|
return (portnum, native_str("tcp:%d" % (portnum,)))
|
2019-04-16 11:26:15 -04:00
|
|
|
|
|
|
|
|
2019-05-01 13:30:14 -04:00
|
|
|
@implementer(IStreamServerEndpoint)
|
|
|
|
@attr.s
|
|
|
|
class CleanupEndpoint(object):
|
2019-05-01 15:47:44 -04:00
|
|
|
"""
|
|
|
|
An ``IStreamServerEndpoint`` wrapper which closes a file descriptor if the
|
|
|
|
wrapped endpoint is never used.
|
|
|
|
|
|
|
|
:ivar IStreamServerEndpoint _wrapped: The wrapped endpoint. The
|
|
|
|
``listen`` implementation is delegated to this object.
|
|
|
|
|
|
|
|
:ivar int _fd: The file descriptor to close if ``listen`` is never called
|
|
|
|
by the time this object is garbage collected.
|
|
|
|
|
|
|
|
:ivar bool _listened: A flag recording whether or not ``listen`` has been
|
|
|
|
called.
|
|
|
|
"""
|
2019-05-01 13:30:14 -04:00
|
|
|
_wrapped = attr.ib()
|
|
|
|
_fd = attr.ib()
|
|
|
|
_listened = attr.ib(default=False)
|
|
|
|
|
|
|
|
def listen(self, protocolFactory):
|
|
|
|
self._listened = True
|
|
|
|
return self._wrapped.listen(protocolFactory)
|
|
|
|
|
|
|
|
def __del__(self):
|
2019-05-01 15:47:44 -04:00
|
|
|
"""
|
|
|
|
If ``listen`` was never called then close the file descriptor.
|
|
|
|
"""
|
2019-05-01 13:30:14 -04:00
|
|
|
if not self._listened:
|
|
|
|
os.close(self._fd)
|
2019-05-01 15:47:44 -04:00
|
|
|
fileDescriptorResource.release()
|
2019-05-01 13:30:14 -04:00
|
|
|
|
|
|
|
|
2019-04-16 11:26:15 -04:00
|
|
|
def listenOnUnused(tub, portnum=None):
|
|
|
|
"""
|
|
|
|
Start listening on an unused TCP port number with the given tub.
|
|
|
|
|
|
|
|
:param portnum: Either an integer port number indicating which TCP/IPv4
|
|
|
|
port number the endpoint should bind or ``None`` to automatically
|
|
|
|
allocate such a port number.
|
|
|
|
|
|
|
|
:return: An integer indicating the TCP port number on which the tub is now
|
|
|
|
listening.
|
|
|
|
"""
|
|
|
|
portnum, endpoint = _foolscapEndpointForPortNumber(portnum)
|
|
|
|
tub.listenOn(endpoint)
|
2020-07-27 15:12:09 -04:00
|
|
|
tub.setLocation(native_str("localhost:%d" % (portnum,)))
|
2019-04-16 11:26:15 -04:00
|
|
|
return portnum
|
|
|
|
|
|
|
|
|
2016-09-01 22:39:03 -07:00
|
|
|
__all__ = ["allocate_tcp_port",
|
|
|
|
"increase_rlimits",
|
|
|
|
"get_local_addresses_sync",
|
|
|
|
"get_local_addresses_async",
|
|
|
|
"get_local_ip_for",
|
2020-07-27 13:06:41 -04:00
|
|
|
"listenOnUnused",
|
2016-09-01 22:39:03 -07:00
|
|
|
]
|