Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

server.py 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. # -*- test-case-name: twisted.names.test.test_names,twisted.names.test.test_server -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Async DNS server
  6. Future plans:
  7. - Better config file format maybe
  8. - Make sure to differentiate between different classes
  9. - notice truncation bit
  10. Important: No additional processing is done on some of the record types.
  11. This violates the most basic RFC and is just plain annoying
  12. for resolvers to deal with. Fix it.
  13. @author: Jp Calderone
  14. """
  15. import time
  16. from twisted.internet import protocol
  17. from twisted.names import dns, resolve
  18. from twisted.python import log
  19. class DNSServerFactory(protocol.ServerFactory):
  20. """
  21. Server factory and tracker for L{DNSProtocol} connections. This class also
  22. provides records for responses to DNS queries.
  23. @ivar cache: A L{Cache<twisted.names.cache.CacheResolver>} instance whose
  24. C{cacheResult} method is called when a response is received from one of
  25. C{clients}. Defaults to L{None} if no caches are specified. See
  26. C{caches} of L{__init__} for more details.
  27. @type cache: L{Cache<twisted.names.cache.CacheResolver>} or L{None}
  28. @ivar canRecurse: A flag indicating whether this server is capable of
  29. performing recursive DNS resolution.
  30. @type canRecurse: L{bool}
  31. @ivar resolver: A L{resolve.ResolverChain} containing an ordered list of
  32. C{authorities}, C{caches} and C{clients} to which queries will be
  33. dispatched.
  34. @type resolver: L{resolve.ResolverChain}
  35. @ivar verbose: See L{__init__}
  36. @ivar connections: A list of all the connected L{DNSProtocol} instances
  37. using this object as their controller.
  38. @type connections: C{list} of L{DNSProtocol} instances
  39. @ivar protocol: A callable used for building a DNS stream protocol. Called
  40. by L{DNSServerFactory.buildProtocol} and passed the L{DNSServerFactory}
  41. instance as the one and only positional argument. Defaults to
  42. L{dns.DNSProtocol}.
  43. @type protocol: L{IProtocolFactory} constructor
  44. @ivar _messageFactory: A response message constructor with an initializer
  45. signature matching L{dns.Message.__init__}.
  46. @type _messageFactory: C{callable}
  47. """
  48. # Type is wrong. See: https://twistedmatrix.com/trac/ticket/10004#ticket
  49. protocol = dns.DNSProtocol # type: ignore[assignment]
  50. cache = None
  51. _messageFactory = dns.Message
  52. def __init__(self, authorities=None, caches=None, clients=None, verbose=0):
  53. """
  54. @param authorities: Resolvers which provide authoritative answers.
  55. @type authorities: L{list} of L{IResolver} providers
  56. @param caches: Resolvers which provide cached non-authoritative
  57. answers. The first cache instance is assigned to
  58. C{DNSServerFactory.cache} and its C{cacheResult} method will be
  59. called when a response is received from one of C{clients}.
  60. @type caches: L{list} of L{Cache<twisted.names.cache.CacheResolver>} instances
  61. @param clients: Resolvers which are capable of performing recursive DNS
  62. lookups.
  63. @type clients: L{list} of L{IResolver} providers
  64. @param verbose: An integer controlling the verbosity of logging of
  65. queries and responses. Default is C{0} which means no logging. Set
  66. to C{2} to enable logging of full query and response messages.
  67. @type verbose: L{int}
  68. """
  69. resolvers = []
  70. if authorities is not None:
  71. resolvers.extend(authorities)
  72. if caches is not None:
  73. resolvers.extend(caches)
  74. if clients is not None:
  75. resolvers.extend(clients)
  76. self.canRecurse = not not clients
  77. self.resolver = resolve.ResolverChain(resolvers)
  78. self.verbose = verbose
  79. if caches:
  80. self.cache = caches[-1]
  81. self.connections = []
  82. def _verboseLog(self, *args, **kwargs):
  83. """
  84. Log a message only if verbose logging is enabled.
  85. @param args: Positional arguments which will be passed to C{log.msg}
  86. @param kwargs: Keyword arguments which will be passed to C{log.msg}
  87. """
  88. if self.verbose > 0:
  89. log.msg(*args, **kwargs)
  90. def buildProtocol(self, addr):
  91. p = self.protocol(self)
  92. p.factory = self
  93. return p
  94. def connectionMade(self, protocol):
  95. """
  96. Track a newly connected L{DNSProtocol}.
  97. @param protocol: The protocol instance to be tracked.
  98. @type protocol: L{dns.DNSProtocol}
  99. """
  100. self.connections.append(protocol)
  101. def connectionLost(self, protocol):
  102. """
  103. Stop tracking a no-longer connected L{DNSProtocol}.
  104. @param protocol: The tracked protocol instance to be which has been
  105. lost.
  106. @type protocol: L{dns.DNSProtocol}
  107. """
  108. self.connections.remove(protocol)
  109. def sendReply(self, protocol, message, address):
  110. """
  111. Send a response C{message} to a given C{address} via the supplied
  112. C{protocol}.
  113. Message payload will be logged if C{DNSServerFactory.verbose} is C{>1}.
  114. @param protocol: The DNS protocol instance to which to send the message.
  115. @type protocol: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  116. @param message: The DNS message to be sent.
  117. @type message: L{dns.Message}
  118. @param address: The address to which the message will be sent or L{None}
  119. if C{protocol} is a stream protocol.
  120. @type address: L{tuple} or L{None}
  121. """
  122. if self.verbose > 1:
  123. s = " ".join([str(a.payload) for a in message.answers])
  124. auth = " ".join([str(a.payload) for a in message.authority])
  125. add = " ".join([str(a.payload) for a in message.additional])
  126. if not s:
  127. log.msg("Replying with no answers")
  128. else:
  129. log.msg("Answers are " + s)
  130. log.msg("Authority is " + auth)
  131. log.msg("Additional is " + add)
  132. if address is None:
  133. protocol.writeMessage(message)
  134. else:
  135. protocol.writeMessage(message, address)
  136. self._verboseLog(
  137. "Processed query in %0.3f seconds" % (time.time() - message.timeReceived)
  138. )
  139. def _responseFromMessage(
  140. self, message, rCode=dns.OK, answers=None, authority=None, additional=None
  141. ):
  142. """
  143. Generate a L{Message} instance suitable for use as the response to
  144. C{message}.
  145. C{queries} will be copied from the request to the response.
  146. C{rCode}, C{answers}, C{authority} and C{additional} will be assigned to
  147. the response, if supplied.
  148. The C{recAv} flag will be set on the response if the C{canRecurse} flag
  149. on this L{DNSServerFactory} is set to L{True}.
  150. The C{auth} flag will be set on the response if *any* of the supplied
  151. C{answers} have their C{auth} flag set to L{True}.
  152. The response will have the same C{maxSize} as the request.
  153. Additionally, the response will have a C{timeReceived} attribute whose
  154. value is that of the original request and the
  155. @see: L{dns._responseFromMessage}
  156. @param message: The request message
  157. @type message: L{Message}
  158. @param rCode: The response code which will be assigned to the response.
  159. @type message: L{int}
  160. @param answers: An optional list of answer records which will be
  161. assigned to the response.
  162. @type answers: L{list} of L{dns.RRHeader}
  163. @param authority: An optional list of authority records which will be
  164. assigned to the response.
  165. @type authority: L{list} of L{dns.RRHeader}
  166. @param additional: An optional list of additional records which will be
  167. assigned to the response.
  168. @type additional: L{list} of L{dns.RRHeader}
  169. @return: A response L{Message} instance.
  170. @rtype: L{Message}
  171. """
  172. if answers is None:
  173. answers = []
  174. if authority is None:
  175. authority = []
  176. if additional is None:
  177. additional = []
  178. authoritativeAnswer = False
  179. for x in answers:
  180. if x.isAuthoritative():
  181. authoritativeAnswer = True
  182. break
  183. response = dns._responseFromMessage(
  184. responseConstructor=self._messageFactory,
  185. message=message,
  186. recAv=self.canRecurse,
  187. rCode=rCode,
  188. auth=authoritativeAnswer,
  189. )
  190. # XXX: Timereceived is a hack which probably shouldn't be tacked onto
  191. # the message. Use getattr here so that we don't have to set the
  192. # timereceived on every message in the tests. See #6957.
  193. response.timeReceived = getattr(message, "timeReceived", None)
  194. # XXX: This is another hack. dns.Message.decode sets maxSize=0 which
  195. # means that responses are never truncated. I'll maintain that behaviour
  196. # here until #6949 is resolved.
  197. response.maxSize = message.maxSize
  198. response.answers = answers
  199. response.authority = authority
  200. response.additional = additional
  201. return response
  202. def gotResolverResponse(self, response, protocol, message, address):
  203. """
  204. A callback used by L{DNSServerFactory.handleQuery} for handling the
  205. deferred response from C{self.resolver.query}.
  206. Constructs a response message by combining the original query message
  207. with the resolved answer, authority and additional records.
  208. Marks the response message as authoritative if any of the resolved
  209. answers are found to be authoritative.
  210. The resolved answers count will be logged if C{DNSServerFactory.verbose}
  211. is C{>1}.
  212. @param response: Answer records, authority records and additional records
  213. @type response: L{tuple} of L{list} of L{dns.RRHeader} instances
  214. @param protocol: The DNS protocol instance to which to send a response
  215. message.
  216. @type protocol: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  217. @param message: The original DNS query message for which a response
  218. message will be constructed.
  219. @type message: L{dns.Message}
  220. @param address: The address to which the response message will be sent
  221. or L{None} if C{protocol} is a stream protocol.
  222. @type address: L{tuple} or L{None}
  223. """
  224. ans, auth, add = response
  225. response = self._responseFromMessage(
  226. message=message, rCode=dns.OK, answers=ans, authority=auth, additional=add
  227. )
  228. self.sendReply(protocol, response, address)
  229. l = len(ans) + len(auth) + len(add)
  230. self._verboseLog("Lookup found %d record%s" % (l, l != 1 and "s" or ""))
  231. if self.cache and l:
  232. self.cache.cacheResult(message.queries[0], (ans, auth, add))
  233. def gotResolverError(self, failure, protocol, message, address):
  234. """
  235. A callback used by L{DNSServerFactory.handleQuery} for handling deferred
  236. errors from C{self.resolver.query}.
  237. Constructs a response message from the original query message by
  238. assigning a suitable error code to C{rCode}.
  239. An error message will be logged if C{DNSServerFactory.verbose} is C{>1}.
  240. @param failure: The reason for the failed resolution (as reported by
  241. C{self.resolver.query}).
  242. @type failure: L{Failure<twisted.python.failure.Failure>}
  243. @param protocol: The DNS protocol instance to which to send a response
  244. message.
  245. @type protocol: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  246. @param message: The original DNS query message for which a response
  247. message will be constructed.
  248. @type message: L{dns.Message}
  249. @param address: The address to which the response message will be sent
  250. or L{None} if C{protocol} is a stream protocol.
  251. @type address: L{tuple} or L{None}
  252. """
  253. if failure.check(dns.DomainError, dns.AuthoritativeDomainError):
  254. rCode = dns.ENAME
  255. else:
  256. rCode = dns.ESERVER
  257. log.err(failure)
  258. response = self._responseFromMessage(message=message, rCode=rCode)
  259. self.sendReply(protocol, response, address)
  260. self._verboseLog("Lookup failed")
  261. def handleQuery(self, message, protocol, address):
  262. """
  263. Called by L{DNSServerFactory.messageReceived} when a query message is
  264. received.
  265. Takes the first query from the received message and dispatches it to
  266. C{self.resolver.query}.
  267. Adds callbacks L{DNSServerFactory.gotResolverResponse} and
  268. L{DNSServerFactory.gotResolverError} to the resulting deferred.
  269. Note: Multiple queries in a single message are not supported because
  270. there is no standard way to respond with multiple rCodes, auth,
  271. etc. This is consistent with other DNS server implementations. See
  272. U{http://tools.ietf.org/html/draft-ietf-dnsext-edns1-03} for a proposed
  273. solution.
  274. @param protocol: The DNS protocol instance to which to send a response
  275. message.
  276. @type protocol: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  277. @param message: The original DNS query message for which a response
  278. message will be constructed.
  279. @type message: L{dns.Message}
  280. @param address: The address to which the response message will be sent
  281. or L{None} if C{protocol} is a stream protocol.
  282. @type address: L{tuple} or L{None}
  283. @return: A C{deferred} which fires with the resolved result or error of
  284. the first query in C{message}.
  285. @rtype: L{Deferred<twisted.internet.defer.Deferred>}
  286. """
  287. query = message.queries[0]
  288. return (
  289. self.resolver.query(query)
  290. .addCallback(self.gotResolverResponse, protocol, message, address)
  291. .addErrback(self.gotResolverError, protocol, message, address)
  292. )
  293. def handleInverseQuery(self, message, protocol, address):
  294. """
  295. Called by L{DNSServerFactory.messageReceived} when an inverse query
  296. message is received.
  297. Replies with a I{Not Implemented} error by default.
  298. An error message will be logged if C{DNSServerFactory.verbose} is C{>1}.
  299. Override in a subclass.
  300. @param protocol: The DNS protocol instance to which to send a response
  301. message.
  302. @type protocol: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  303. @param message: The original DNS query message for which a response
  304. message will be constructed.
  305. @type message: L{dns.Message}
  306. @param address: The address to which the response message will be sent
  307. or L{None} if C{protocol} is a stream protocol.
  308. @type address: L{tuple} or L{None}
  309. """
  310. message.rCode = dns.ENOTIMP
  311. self.sendReply(protocol, message, address)
  312. self._verboseLog(f"Inverse query from {address!r}")
  313. def handleStatus(self, message, protocol, address):
  314. """
  315. Called by L{DNSServerFactory.messageReceived} when a status message is
  316. received.
  317. Replies with a I{Not Implemented} error by default.
  318. An error message will be logged if C{DNSServerFactory.verbose} is C{>1}.
  319. Override in a subclass.
  320. @param protocol: The DNS protocol instance to which to send a response
  321. message.
  322. @type protocol: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  323. @param message: The original DNS query message for which a response
  324. message will be constructed.
  325. @type message: L{dns.Message}
  326. @param address: The address to which the response message will be sent
  327. or L{None} if C{protocol} is a stream protocol.
  328. @type address: L{tuple} or L{None}
  329. """
  330. message.rCode = dns.ENOTIMP
  331. self.sendReply(protocol, message, address)
  332. self._verboseLog(f"Status request from {address!r}")
  333. def handleNotify(self, message, protocol, address):
  334. """
  335. Called by L{DNSServerFactory.messageReceived} when a notify message is
  336. received.
  337. Replies with a I{Not Implemented} error by default.
  338. An error message will be logged if C{DNSServerFactory.verbose} is C{>1}.
  339. Override in a subclass.
  340. @param protocol: The DNS protocol instance to which to send a response
  341. message.
  342. @type protocol: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  343. @param message: The original DNS query message for which a response
  344. message will be constructed.
  345. @type message: L{dns.Message}
  346. @param address: The address to which the response message will be sent
  347. or L{None} if C{protocol} is a stream protocol.
  348. @type address: L{tuple} or L{None}
  349. """
  350. message.rCode = dns.ENOTIMP
  351. self.sendReply(protocol, message, address)
  352. self._verboseLog(f"Notify message from {address!r}")
  353. def handleOther(self, message, protocol, address):
  354. """
  355. Called by L{DNSServerFactory.messageReceived} when a message with
  356. unrecognised I{OPCODE} is received.
  357. Replies with a I{Not Implemented} error by default.
  358. An error message will be logged if C{DNSServerFactory.verbose} is C{>1}.
  359. Override in a subclass.
  360. @param protocol: The DNS protocol instance to which to send a response
  361. message.
  362. @type protocol: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  363. @param message: The original DNS query message for which a response
  364. message will be constructed.
  365. @type message: L{dns.Message}
  366. @param address: The address to which the response message will be sent
  367. or L{None} if C{protocol} is a stream protocol.
  368. @type address: L{tuple} or L{None}
  369. """
  370. message.rCode = dns.ENOTIMP
  371. self.sendReply(protocol, message, address)
  372. self._verboseLog("Unknown op code (%d) from %r" % (message.opCode, address))
  373. def messageReceived(self, message, proto, address=None):
  374. """
  375. L{DNSServerFactory.messageReceived} is called by protocols which are
  376. under the control of this L{DNSServerFactory} whenever they receive a
  377. DNS query message or an unexpected / duplicate / late DNS response
  378. message.
  379. L{DNSServerFactory.allowQuery} is called with the received message,
  380. protocol and origin address. If it returns L{False}, a C{dns.EREFUSED}
  381. response is sent back to the client.
  382. Otherwise the received message is dispatched to one of
  383. L{DNSServerFactory.handleQuery}, L{DNSServerFactory.handleInverseQuery},
  384. L{DNSServerFactory.handleStatus}, L{DNSServerFactory.handleNotify}, or
  385. L{DNSServerFactory.handleOther} depending on the I{OPCODE} of the
  386. received message.
  387. If C{DNSServerFactory.verbose} is C{>0} all received messages will be
  388. logged in more or less detail depending on the value of C{verbose}.
  389. @param message: The DNS message that was received.
  390. @type message: L{dns.Message}
  391. @param proto: The DNS protocol instance which received the message
  392. @type proto: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  393. @param address: The address from which the message was received. Only
  394. provided for messages received by datagram protocols. The origin of
  395. Messages received from stream protocols can be gleaned from the
  396. protocol C{transport} attribute.
  397. @type address: L{tuple} or L{None}
  398. """
  399. message.timeReceived = time.time()
  400. if self.verbose:
  401. if self.verbose > 1:
  402. s = " ".join([str(q) for q in message.queries])
  403. else:
  404. s = " ".join(
  405. [dns.QUERY_TYPES.get(q.type, "UNKNOWN") for q in message.queries]
  406. )
  407. if not len(s):
  408. log.msg(f"Empty query from {address or proto.transport.getPeer()!r}")
  409. else:
  410. log.msg(f"{s} query from {address or proto.transport.getPeer()!r}")
  411. if not self.allowQuery(message, proto, address):
  412. message.rCode = dns.EREFUSED
  413. self.sendReply(proto, message, address)
  414. elif message.opCode == dns.OP_QUERY:
  415. self.handleQuery(message, proto, address)
  416. elif message.opCode == dns.OP_INVERSE:
  417. self.handleInverseQuery(message, proto, address)
  418. elif message.opCode == dns.OP_STATUS:
  419. self.handleStatus(message, proto, address)
  420. elif message.opCode == dns.OP_NOTIFY:
  421. self.handleNotify(message, proto, address)
  422. else:
  423. self.handleOther(message, proto, address)
  424. def allowQuery(self, message, protocol, address):
  425. """
  426. Called by L{DNSServerFactory.messageReceived} to decide whether to
  427. process a received message or to reply with C{dns.EREFUSED}.
  428. This default implementation permits anything but empty queries.
  429. Override in a subclass to implement alternative policies.
  430. @param message: The DNS message that was received.
  431. @type message: L{dns.Message}
  432. @param protocol: The DNS protocol instance which received the message
  433. @type protocol: L{dns.DNSDatagramProtocol} or L{dns.DNSProtocol}
  434. @param address: The address from which the message was received. Only
  435. provided for messages received by datagram protocols. The origin of
  436. Messages received from stream protocols can be gleaned from the
  437. protocol C{transport} attribute.
  438. @type address: L{tuple} or L{None}
  439. @return: L{True} if the received message contained one or more queries,
  440. else L{False}.
  441. @rtype: L{bool}
  442. """
  443. return len(message.queries)