address.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Address objects for network connections.
  5. """
  6. from __future__ import division, absolute_import
  7. import warnings, os
  8. from zope.interface import implementer
  9. from twisted.internet.interfaces import IAddress
  10. from twisted.python.filepath import _asFilesystemBytes
  11. from twisted.python.filepath import _coerceToFilesystemEncoding
  12. from twisted.python.util import FancyEqMixin
  13. from twisted.python.runtime import platform
  14. from twisted.python.compat import _PY3
  15. @implementer(IAddress)
  16. class _IPAddress(FancyEqMixin, object):
  17. """
  18. An L{_IPAddress} represents the address of an IP socket endpoint, providing
  19. common behavior for IPv4 and IPv6.
  20. @ivar type: A string describing the type of transport, either 'TCP' or
  21. 'UDP'.
  22. @ivar host: A string containing the presentation format of the IP address;
  23. for example, "127.0.0.1" or "::1".
  24. @type host: C{str}
  25. @ivar port: An integer representing the port number.
  26. @type port: C{int}
  27. """
  28. compareAttributes = ('type', 'host', 'port')
  29. def __init__(self, type, host, port):
  30. assert type in ('TCP', 'UDP')
  31. self.type = type
  32. self.host = host
  33. self.port = port
  34. def __repr__(self):
  35. return '%s(%s, %r, %d)' % (
  36. self.__class__.__name__, self.type, self.host, self.port)
  37. def __hash__(self):
  38. return hash((self.type, self.host, self.port))
  39. class IPv4Address(_IPAddress):
  40. """
  41. An L{IPv4Address} represents the address of an IPv4 socket endpoint.
  42. @ivar host: A string containing a dotted-quad IPv4 address; for example,
  43. "127.0.0.1".
  44. @type host: C{str}
  45. """
  46. def __init__(self, type, host, port, _bwHack=None):
  47. _IPAddress.__init__(self, type, host, port)
  48. if _bwHack is not None:
  49. warnings.warn("twisted.internet.address.IPv4Address._bwHack "
  50. "is deprecated since Twisted 11.0",
  51. DeprecationWarning, stacklevel=2)
  52. class IPv6Address(_IPAddress):
  53. """
  54. An L{IPv6Address} represents the address of an IPv6 socket endpoint.
  55. @ivar host: A string containing a colon-separated, hexadecimal formatted
  56. IPv6 address; for example, "::1".
  57. @type host: C{str}
  58. @ivar flowInfo: the IPv6 flow label. This can be used by QoS routers to
  59. identify flows of traffic; you may generally safely ignore it.
  60. @type flowInfo: L{int}
  61. @ivar scopeID: the IPv6 scope identifier - roughly analagous to what
  62. interface traffic destined for this address must be transmitted over.
  63. @type scopeID: L{int}
  64. """
  65. compareAttributes = ('type', 'host', 'port', 'flowInfo', 'scopeID')
  66. def __init__(self, type, host, port, flowInfo=0, scopeID=0):
  67. super(IPv6Address, self).__init__(type, host, port)
  68. self.flowInfo = flowInfo
  69. self.scopeID = scopeID
  70. @implementer(IAddress)
  71. class _ProcessAddress(object):
  72. """
  73. An L{interfaces.IAddress} provider for process transports.
  74. """
  75. @implementer(IAddress)
  76. class HostnameAddress(FancyEqMixin, object):
  77. """
  78. A L{HostnameAddress} represents the address of a L{HostnameEndpoint}.
  79. @ivar hostname: A hostname byte string; for example, b"example.com".
  80. @type hostname: L{bytes}
  81. @ivar port: An integer representing the port number.
  82. @type port: L{int}
  83. """
  84. compareAttributes = ('hostname', 'port')
  85. def __init__(self, hostname, port):
  86. self.hostname = hostname
  87. self.port = port
  88. def __repr__(self):
  89. return '%s(%s, %d)' % (
  90. self.__class__.__name__, self.hostname, self.port)
  91. def __hash__(self):
  92. return hash((self.hostname, self.port))
  93. @implementer(IAddress)
  94. class UNIXAddress(FancyEqMixin, object):
  95. """
  96. Object representing a UNIX socket endpoint.
  97. @ivar name: The filename associated with this socket.
  98. @type name: C{bytes}
  99. """
  100. compareAttributes = ('name', )
  101. def __init__(self, name, _bwHack = None):
  102. self.name = name
  103. if _bwHack is not None:
  104. warnings.warn("twisted.internet.address.UNIXAddress._bwHack is deprecated since Twisted 11.0",
  105. DeprecationWarning, stacklevel=2)
  106. @property
  107. def name(self):
  108. return self._name
  109. @name.setter
  110. def name(self, name):
  111. """
  112. On UNIX, paths are always bytes. However, as paths are L{unicode} on
  113. Python 3, and L{UNIXAddress} technically takes a file path, we convert
  114. it to bytes to maintain compatibility with C{os.path} on Python 3.
  115. """
  116. if name is not None:
  117. self._name = _asFilesystemBytes(name)
  118. else:
  119. self._name = None
  120. if getattr(os.path, 'samefile', None) is not None:
  121. def __eq__(self, other):
  122. """
  123. Overriding C{FancyEqMixin} to ensure the os level samefile
  124. check is done if the name attributes do not match.
  125. """
  126. res = super(UNIXAddress, self).__eq__(other)
  127. if not res and self.name and other.name:
  128. try:
  129. return os.path.samefile(self.name, other.name)
  130. except OSError:
  131. pass
  132. except (TypeError, ValueError) as e:
  133. # On Linux, abstract namespace UNIX sockets start with a
  134. # \0, which os.path doesn't like.
  135. if not _PY3 and not platform.isLinux():
  136. raise e
  137. return res
  138. def __repr__(self):
  139. name = self.name
  140. if name:
  141. name = _coerceToFilesystemEncoding('', self.name)
  142. return 'UNIXAddress(%r)' % (name,)
  143. def __hash__(self):
  144. if self.name is None:
  145. return hash((self.__class__, None))
  146. try:
  147. s1 = os.stat(self.name)
  148. return hash((s1.st_ino, s1.st_dev))
  149. except OSError:
  150. return hash(self.name)
  151. # These are for buildFactory backwards compatibility due to
  152. # stupidity-induced inconsistency.
  153. class _ServerFactoryIPv4Address(IPv4Address):
  154. """Backwards compatibility hack. Just like IPv4Address in practice."""
  155. def __eq__(self, other):
  156. if isinstance(other, tuple):
  157. warnings.warn("IPv4Address.__getitem__ is deprecated. Use attributes instead.",
  158. category=DeprecationWarning, stacklevel=2)
  159. return (self.host, self.port) == other
  160. elif isinstance(other, IPv4Address):
  161. a = (self.type, self.host, self.port)
  162. b = (other.type, other.host, other.port)
  163. return a == b
  164. return False