You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

184 regels
5.1 KiB

  1. # This is a direct lift from
  2. # https://github.com/twisted/twisted/blob/release-21.2.0-10091/src/twisted/internet/_resolver.py.
  3. # We copy it here as we need to instantiate `GAIResolver` manually, but it is a
  4. # private class.
  5. from socket import (
  6. AF_INET,
  7. AF_INET6,
  8. AF_UNSPEC,
  9. SOCK_DGRAM,
  10. SOCK_STREAM,
  11. AddressFamily,
  12. SocketKind,
  13. gaierror,
  14. getaddrinfo,
  15. )
  16. from typing import (
  17. TYPE_CHECKING,
  18. Callable,
  19. List,
  20. NoReturn,
  21. Optional,
  22. Sequence,
  23. Tuple,
  24. Type,
  25. Union,
  26. )
  27. from zope.interface import implementer
  28. from twisted.internet.address import IPv4Address, IPv6Address
  29. from twisted.internet.interfaces import (
  30. IAddress,
  31. IHostnameResolver,
  32. IHostResolution,
  33. IReactorThreads,
  34. IResolutionReceiver,
  35. )
  36. from twisted.internet.threads import deferToThreadPool
  37. if TYPE_CHECKING:
  38. # The types below are copied from
  39. # https://github.com/twisted/twisted/blob/release-21.2.0-10091/src/twisted/internet/interfaces.py
  40. # so that the type hints can match the interfaces.
  41. from twisted.python.runtime import platform
  42. if platform.supportsThreads():
  43. from twisted.python.threadpool import ThreadPool
  44. else:
  45. ThreadPool = object # type: ignore[misc, assignment]
  46. @implementer(IHostResolution)
  47. class HostResolution:
  48. """
  49. The in-progress resolution of a given hostname.
  50. """
  51. def __init__(self, name: str):
  52. """
  53. Create a L{HostResolution} with the given name.
  54. """
  55. self.name = name
  56. def cancel(self) -> NoReturn:
  57. # IHostResolution.cancel
  58. raise NotImplementedError()
  59. _any = frozenset([IPv4Address, IPv6Address])
  60. _typesToAF = {
  61. frozenset([IPv4Address]): AF_INET,
  62. frozenset([IPv6Address]): AF_INET6,
  63. _any: AF_UNSPEC,
  64. }
  65. _afToType = {
  66. AF_INET: IPv4Address,
  67. AF_INET6: IPv6Address,
  68. }
  69. _transportToSocket = {
  70. "TCP": SOCK_STREAM,
  71. "UDP": SOCK_DGRAM,
  72. }
  73. _socktypeToType = {
  74. SOCK_STREAM: "TCP",
  75. SOCK_DGRAM: "UDP",
  76. }
  77. _GETADDRINFO_RESULT = List[
  78. Tuple[
  79. AddressFamily,
  80. SocketKind,
  81. int,
  82. str,
  83. Union[Tuple[str, int], Tuple[str, int, int, int]],
  84. ]
  85. ]
  86. @implementer(IHostnameResolver)
  87. class GAIResolver:
  88. """
  89. L{IHostnameResolver} implementation that resolves hostnames by calling
  90. L{getaddrinfo} in a thread.
  91. """
  92. def __init__(
  93. self,
  94. reactor: IReactorThreads,
  95. getThreadPool: Optional[Callable[[], "ThreadPool"]] = None,
  96. getaddrinfo: Callable[[str, int, int, int], _GETADDRINFO_RESULT] = getaddrinfo,
  97. ):
  98. """
  99. Create a L{GAIResolver}.
  100. @param reactor: the reactor to schedule result-delivery on
  101. @type reactor: L{IReactorThreads}
  102. @param getThreadPool: a function to retrieve the thread pool to use for
  103. scheduling name resolutions. If not supplied, the use the given
  104. C{reactor}'s thread pool.
  105. @type getThreadPool: 0-argument callable returning a
  106. L{twisted.python.threadpool.ThreadPool}
  107. @param getaddrinfo: a reference to the L{getaddrinfo} to use - mainly
  108. parameterized for testing.
  109. @type getaddrinfo: callable with the same signature as L{getaddrinfo}
  110. """
  111. self._reactor = reactor
  112. self._getThreadPool = (
  113. reactor.getThreadPool if getThreadPool is None else getThreadPool
  114. )
  115. self._getaddrinfo = getaddrinfo
  116. # The types on IHostnameResolver is incorrect in Twisted, see
  117. # https://twistedmatrix.com/trac/ticket/10276
  118. def resolveHostName(
  119. self,
  120. resolutionReceiver: IResolutionReceiver,
  121. hostName: str,
  122. portNumber: int = 0,
  123. addressTypes: Optional[Sequence[Type[IAddress]]] = None,
  124. transportSemantics: str = "TCP",
  125. ) -> IHostResolution:
  126. """
  127. See L{IHostnameResolver.resolveHostName}
  128. @param resolutionReceiver: see interface
  129. @param hostName: see interface
  130. @param portNumber: see interface
  131. @param addressTypes: see interface
  132. @param transportSemantics: see interface
  133. @return: see interface
  134. """
  135. pool = self._getThreadPool()
  136. addressFamily = _typesToAF[
  137. _any if addressTypes is None else frozenset(addressTypes)
  138. ]
  139. socketType = _transportToSocket[transportSemantics]
  140. def get() -> _GETADDRINFO_RESULT:
  141. try:
  142. return self._getaddrinfo(
  143. hostName, portNumber, addressFamily, socketType
  144. )
  145. except gaierror:
  146. return []
  147. d = deferToThreadPool(self._reactor, pool, get)
  148. resolution = HostResolution(hostName)
  149. resolutionReceiver.resolutionBegan(resolution)
  150. @d.addCallback
  151. def deliverResults(result: _GETADDRINFO_RESULT) -> None:
  152. for family, socktype, _proto, _cannoname, sockaddr in result:
  153. addrType = _afToType[family]
  154. resolutionReceiver.addressResolved(
  155. addrType(_socktypeToType.get(socktype, "TCP"), *sockaddr)
  156. )
  157. resolutionReceiver.resolutionComplete()
  158. return resolution