xfrout_test.py.in 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. # Copyright (C) 2010 Internet Systems Consortium.
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. '''Tests for the XfroutSession and UnixSockServer classes '''
  16. import unittest
  17. import os
  18. from isc.testutils.tsigctx_mock import MockTSIGContext
  19. from isc.cc.session import *
  20. import isc.config
  21. from isc.dns import *
  22. from xfrout import *
  23. import xfrout
  24. import isc.log
  25. import isc.acl.dns
  26. TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
  27. # our fake socket, where we can read and insert messages
  28. class MySocket():
  29. def __init__(self, family, type):
  30. self.family = family
  31. self.type = type
  32. self.sendqueue = bytearray()
  33. def connect(self, to):
  34. pass
  35. def close(self):
  36. pass
  37. def send(self, data):
  38. self.sendqueue.extend(data);
  39. return len(data)
  40. def readsent(self):
  41. if len(self.sendqueue) >= 2:
  42. size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
  43. else:
  44. size = 0
  45. result = self.sendqueue[:size]
  46. self.sendqueue = self.sendqueue[size:]
  47. return result
  48. def read_msg(self):
  49. sent_data = self.readsent()
  50. get_msg = Message(Message.PARSE)
  51. get_msg.from_wire(bytes(sent_data[2:]))
  52. return get_msg
  53. def clear_send(self):
  54. del self.sendqueue[:]
  55. class MockDataSrcClient:
  56. def __init__(self, type, config):
  57. pass
  58. def get_iterator(self, zone_name):
  59. if zone_name == Name('notauth.example.com'):
  60. raise isc.datasrc.Error('no such zone')
  61. self._zone_name = zone_name
  62. return self
  63. def get_soa(self): # emulate ZoneIterator.get_soa()
  64. if self._zone_name == Name('nosoa.example.com'):
  65. return None
  66. soa_rrset = RRset(self._zone_name, RRClass.IN(), RRType.SOA(),
  67. RRTTL(3600))
  68. soa_rrset.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
  69. 'master.example.com. ' +
  70. 'admin.example.com. 1234 ' +
  71. '3600 1800 2419200 7200'))
  72. if self._zone_name == Name('multisoa.example.com'):
  73. soa_rrset.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
  74. 'master.example.com. ' +
  75. 'admin.example.com. 1300 ' +
  76. '3600 1800 2419200 7200'))
  77. return soa_rrset
  78. class MyCCSession(isc.config.ConfigData):
  79. def __init__(self):
  80. module_spec = isc.config.module_spec_from_file(
  81. xfrout.SPECFILE_LOCATION)
  82. ConfigData.__init__(self, module_spec)
  83. def get_remote_config_value(self, module_name, identifier):
  84. if module_name == "Auth" and identifier == "database_file":
  85. return "initdb.file", False
  86. else:
  87. return "unknown", False
  88. # This constant dictionary stores all default configuration parameters
  89. # defined in the xfrout spec file.
  90. DEFAULT_CONFIG = MyCCSession().get_full_config()
  91. # We subclass the Session class we're testing here, only overriding a few
  92. # methods
  93. class MyXfroutSession(XfroutSession):
  94. def _handle(self):
  95. pass
  96. def _close_socket(self):
  97. pass
  98. def _send_data(self, sock, data):
  99. size = len(data)
  100. total_count = 0
  101. while total_count < size:
  102. count = sock.send(data[total_count:])
  103. total_count += count
  104. class Dbserver:
  105. def __init__(self):
  106. self._shutdown_event = threading.Event()
  107. self.transfer_counter = 0
  108. self._max_transfers_out = DEFAULT_CONFIG['transfers_out']
  109. def get_db_file(self):
  110. return 'test.sqlite3'
  111. def increase_transfers_counter(self):
  112. self.transfer_counter += 1
  113. return True
  114. def decrease_transfers_counter(self):
  115. self.transfer_counter -= 1
  116. class TestXfroutSession(unittest.TestCase):
  117. def getmsg(self):
  118. msg = Message(Message.PARSE)
  119. msg.from_wire(self.mdata)
  120. return msg
  121. def create_mock_tsig_ctx(self, error):
  122. # This helper function creates a MockTSIGContext for a given key
  123. # and TSIG error to be used as a result of verify (normally faked
  124. # one)
  125. mock_ctx = MockTSIGContext(TSIG_KEY)
  126. mock_ctx.error = error
  127. return mock_ctx
  128. def message_has_tsig(self, msg):
  129. return msg.get_tsig_record() is not None
  130. def create_request_data(self, with_question=True, with_tsig=False):
  131. msg = Message(Message.RENDER)
  132. query_id = 0x1035
  133. msg.set_qid(query_id)
  134. msg.set_opcode(Opcode.QUERY())
  135. msg.set_rcode(Rcode.NOERROR())
  136. if with_question:
  137. msg.add_question(Question(Name("example.com"), RRClass.IN(),
  138. RRType.AXFR()))
  139. renderer = MessageRenderer()
  140. if with_tsig:
  141. tsig_ctx = MockTSIGContext(TSIG_KEY)
  142. msg.to_wire(renderer, tsig_ctx)
  143. else:
  144. msg.to_wire(renderer)
  145. request_data = renderer.get_data()
  146. return request_data
  147. def setUp(self):
  148. self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
  149. self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(),
  150. TSIGKeyRing(),
  151. (socket.AF_INET, socket.SOCK_STREAM,
  152. ('127.0.0.1', 12345)),
  153. # When not testing ACLs, simply accept
  154. isc.acl.dns.REQUEST_LOADER.load(
  155. [{"action": "ACCEPT"}]),
  156. {})
  157. self.mdata = self.create_request_data()
  158. self.soa_rrset = RRset(Name('example.com'), RRClass.IN(), RRType.SOA(),
  159. RRTTL(3600))
  160. self.soa_rrset.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
  161. 'master.Example.com. ' +
  162. 'admin.exAmple.com. ' +
  163. '1234 3600 1800 2419200 7200'))
  164. def tearDown(self):
  165. # transfer_counter must be always be reset no matter happens within
  166. # the XfroutSession object. We check the condition here.
  167. self.assertEqual(0, self.xfrsess._server.transfer_counter)
  168. def test_quota_error(self):
  169. '''Emulating the server being too busy.
  170. '''
  171. self.xfrsess._request_data = self.mdata
  172. self.xfrsess._server.increase_transfers_counter = lambda : False
  173. XfroutSession._handle(self.xfrsess)
  174. self.assertEqual(self.sock.read_msg().get_rcode(), Rcode.REFUSED())
  175. def test_quota_ok(self):
  176. '''The default case in terms of the xfrout quota.
  177. '''
  178. # set up a bogus request, which should result in FORMERR. (it only
  179. # has to be something that is different from the previous case)
  180. self.xfrsess._request_data = \
  181. self.create_request_data(with_question=False)
  182. # Replace the data source client to avoid datasrc related exceptions
  183. self.xfrsess.ClientClass = MockDataSrcClient
  184. XfroutSession._handle(self.xfrsess)
  185. self.assertEqual(self.sock.read_msg().get_rcode(), Rcode.FORMERR())
  186. def test_exception_from_session(self):
  187. '''Test the case where the main processing raises an exception.
  188. We just check it doesn't any unexpected disruption and (in tearDown)
  189. transfer_counter is correctly reset to 0.
  190. '''
  191. def dns_xfrout_start(fd, msg, quota):
  192. raise ValueError('fake exception')
  193. self.xfrsess.dns_xfrout_start = dns_xfrout_start
  194. XfroutSession._handle(self.xfrsess)
  195. def test_parse_query_message(self):
  196. [get_rcode, get_msg] = self.xfrsess._parse_query_message(self.mdata)
  197. self.assertEqual(get_rcode.to_text(), "NOERROR")
  198. # Broken request: no question
  199. request_data = self.create_request_data(with_question=False)
  200. rcode, msg = self.xfrsess._parse_query_message(request_data)
  201. self.assertEqual(Rcode.FORMERR(), rcode)
  202. # tsig signed query message
  203. request_data = self.create_request_data(with_tsig=True)
  204. # BADKEY
  205. [rcode, msg] = self.xfrsess._parse_query_message(request_data)
  206. self.assertEqual(rcode.to_text(), "NOTAUTH")
  207. self.assertTrue(self.xfrsess._tsig_ctx is not None)
  208. # NOERROR
  209. self.assertEqual(TSIGKeyRing.SUCCESS,
  210. self.xfrsess._tsig_key_ring.add(TSIG_KEY))
  211. [rcode, msg] = self.xfrsess._parse_query_message(request_data)
  212. self.assertEqual(rcode.to_text(), "NOERROR")
  213. self.assertTrue(self.xfrsess._tsig_ctx is not None)
  214. def check_transfer_acl(self, acl_setter):
  215. # ACL checks, put some ACL inside
  216. acl_setter(isc.acl.dns.REQUEST_LOADER.load([
  217. {
  218. "from": "127.0.0.1",
  219. "action": "ACCEPT"
  220. },
  221. {
  222. "from": "192.0.2.1",
  223. "action": "DROP"
  224. }
  225. ]))
  226. # Localhost (the default in this test) is accepted
  227. rcode, msg = self.xfrsess._parse_query_message(self.mdata)
  228. self.assertEqual(rcode.to_text(), "NOERROR")
  229. # This should be dropped completely, therefore returning None
  230. self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
  231. ('192.0.2.1', 12345))
  232. rcode, msg = self.xfrsess._parse_query_message(self.mdata)
  233. self.assertEqual(None, rcode)
  234. # This should be refused, therefore REFUSED
  235. self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
  236. ('192.0.2.2', 12345))
  237. rcode, msg = self.xfrsess._parse_query_message(self.mdata)
  238. self.assertEqual(rcode.to_text(), "REFUSED")
  239. # TSIG signed request
  240. request_data = self.create_request_data(with_tsig=True)
  241. # If the TSIG check fails, it should not check ACL
  242. # (If it checked ACL as well, it would just drop the request)
  243. self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
  244. ('192.0.2.1', 12345))
  245. self.xfrsess._tsig_key_ring = TSIGKeyRing()
  246. rcode, msg = self.xfrsess._parse_query_message(request_data)
  247. self.assertEqual(rcode.to_text(), "NOTAUTH")
  248. self.assertTrue(self.xfrsess._tsig_ctx is not None)
  249. # ACL using TSIG: successful case
  250. acl_setter(isc.acl.dns.REQUEST_LOADER.load([
  251. {"key": "example.com", "action": "ACCEPT"}, {"action": "REJECT"}
  252. ]))
  253. self.assertEqual(TSIGKeyRing.SUCCESS,
  254. self.xfrsess._tsig_key_ring.add(TSIG_KEY))
  255. [rcode, msg] = self.xfrsess._parse_query_message(request_data)
  256. self.assertEqual(rcode.to_text(), "NOERROR")
  257. # ACL using TSIG: key name doesn't match; should be rejected
  258. acl_setter(isc.acl.dns.REQUEST_LOADER.load([
  259. {"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"}
  260. ]))
  261. [rcode, msg] = self.xfrsess._parse_query_message(request_data)
  262. self.assertEqual(rcode.to_text(), "REFUSED")
  263. # ACL using TSIG: no TSIG; should be rejected
  264. acl_setter(isc.acl.dns.REQUEST_LOADER.load([
  265. {"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"}
  266. ]))
  267. [rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
  268. self.assertEqual(rcode.to_text(), "REFUSED")
  269. #
  270. # ACL using IP + TSIG: both should match
  271. #
  272. acl_setter(isc.acl.dns.REQUEST_LOADER.load([
  273. {"ALL": [{"key": "example.com"}, {"from": "192.0.2.1"}],
  274. "action": "ACCEPT"},
  275. {"action": "REJECT"}
  276. ]))
  277. # both matches
  278. self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
  279. ('192.0.2.1', 12345))
  280. [rcode, msg] = self.xfrsess._parse_query_message(request_data)
  281. self.assertEqual(rcode.to_text(), "NOERROR")
  282. # TSIG matches, but address doesn't
  283. self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
  284. ('192.0.2.2', 12345))
  285. [rcode, msg] = self.xfrsess._parse_query_message(request_data)
  286. self.assertEqual(rcode.to_text(), "REFUSED")
  287. # Address matches, but TSIG doesn't (not included)
  288. self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
  289. ('192.0.2.1', 12345))
  290. [rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
  291. self.assertEqual(rcode.to_text(), "REFUSED")
  292. # Neither address nor TSIG matches
  293. self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
  294. ('192.0.2.2', 12345))
  295. [rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
  296. self.assertEqual(rcode.to_text(), "REFUSED")
  297. def test_transfer_acl(self):
  298. # ACL checks only with the default ACL
  299. def acl_setter(acl):
  300. self.xfrsess._acl = acl
  301. self.check_transfer_acl(acl_setter)
  302. def test_transfer_zoneacl(self):
  303. # ACL check with a per zone ACL + default ACL. The per zone ACL
  304. # should match the queryied zone, so it should be used.
  305. def acl_setter(acl):
  306. zone_key = ('IN', 'example.com.')
  307. self.xfrsess._zone_config[zone_key] = {}
  308. self.xfrsess._zone_config[zone_key]['transfer_acl'] = acl
  309. self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
  310. {"from": "127.0.0.1", "action": "DROP"}])
  311. self.check_transfer_acl(acl_setter)
  312. def test_transfer_zoneacl_nomatch(self):
  313. # similar to the previous one, but the per zone doesn't match the
  314. # query. The default should be used.
  315. def acl_setter(acl):
  316. zone_key = ('IN', 'example.org.')
  317. self.xfrsess._zone_config[zone_key] = {}
  318. self.xfrsess._zone_config[zone_key]['transfer_acl'] = \
  319. isc.acl.dns.REQUEST_LOADER.load([
  320. {"from": "127.0.0.1", "action": "DROP"}])
  321. self.xfrsess._acl = acl
  322. self.check_transfer_acl(acl_setter)
  323. def test_get_transfer_acl(self):
  324. # set the default ACL. If there's no specific zone ACL, this one
  325. # should be used.
  326. self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
  327. {"from": "127.0.0.1", "action": "ACCEPT"}])
  328. acl = self.xfrsess._get_transfer_acl(Name('example.com'), RRClass.IN())
  329. self.assertEqual(acl, self.xfrsess._acl)
  330. # install a per zone config with transfer ACL for example.com. Then
  331. # that ACL will be used for example.com; for others the default ACL
  332. # will still be used.
  333. com_acl = isc.acl.dns.REQUEST_LOADER.load([
  334. {"from": "127.0.0.1", "action": "REJECT"}])
  335. self.xfrsess._zone_config[('IN', 'example.com.')] = {}
  336. self.xfrsess._zone_config[('IN', 'example.com.')]['transfer_acl'] = \
  337. com_acl
  338. self.assertEqual(com_acl,
  339. self.xfrsess._get_transfer_acl(Name('example.com'),
  340. RRClass.IN()))
  341. self.assertEqual(self.xfrsess._acl,
  342. self.xfrsess._get_transfer_acl(Name('example.org'),
  343. RRClass.IN()))
  344. # Name matching should be case insensitive.
  345. self.assertEqual(com_acl,
  346. self.xfrsess._get_transfer_acl(Name('EXAMPLE.COM'),
  347. RRClass.IN()))
  348. def test_send_data(self):
  349. self.xfrsess._send_data(self.sock, self.mdata)
  350. senddata = self.sock.readsent()
  351. self.assertEqual(senddata, self.mdata)
  352. def test_reply_xfrout_query_with_error_rcode(self):
  353. msg = self.getmsg()
  354. self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
  355. get_msg = self.sock.read_msg()
  356. self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
  357. # tsig signed message
  358. msg = self.getmsg()
  359. self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
  360. self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
  361. get_msg = self.sock.read_msg()
  362. self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
  363. self.assertTrue(self.message_has_tsig(get_msg))
  364. def test_send_message(self):
  365. msg = self.getmsg()
  366. msg.make_response()
  367. # SOA record data with different cases
  368. soa_rrset = RRset(Name('Example.com.'), RRClass.IN(), RRType.SOA(),
  369. RRTTL(3600))
  370. soa_rrset.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
  371. 'master.Example.com. admin.exAmple.com. ' +
  372. '1234 3600 1800 2419200 7200'))
  373. msg.add_rrset(Message.SECTION_ANSWER, soa_rrset)
  374. self.xfrsess._send_message(self.sock, msg)
  375. send_out_data = self.sock.readsent()[2:]
  376. # CASE_INSENSITIVE compression mode
  377. render = MessageRenderer();
  378. render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
  379. msg.to_wire(render)
  380. self.assertNotEqual(render.get_data(), send_out_data)
  381. # CASE_SENSITIVE compression mode
  382. render.clear()
  383. render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
  384. render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
  385. msg.to_wire(render)
  386. self.assertEqual(render.get_data(), send_out_data)
  387. def test_clear_message(self):
  388. msg = self.getmsg()
  389. qid = msg.get_qid()
  390. opcode = msg.get_opcode()
  391. rcode = msg.get_rcode()
  392. self.xfrsess._clear_message(msg)
  393. self.assertEqual(msg.get_qid(), qid)
  394. self.assertEqual(msg.get_opcode(), opcode)
  395. self.assertEqual(msg.get_rcode(), rcode)
  396. self.assertTrue(msg.get_header_flag(Message.HEADERFLAG_AA))
  397. def test_send_message_with_last_soa(self):
  398. msg = self.getmsg()
  399. msg.make_response()
  400. # packet number less than TSIG_SIGN_EVERY_NTH
  401. packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
  402. self.xfrsess._send_message_with_last_soa(msg, self.sock,
  403. self.soa_rrset, 0,
  404. packet_neet_not_sign)
  405. get_msg = self.sock.read_msg()
  406. # tsig context is not exist
  407. self.assertFalse(self.message_has_tsig(get_msg))
  408. self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
  409. self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
  410. self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
  411. #answer_rrset_iter = section_iter(get_msg, section.ANSWER())
  412. answer = get_msg.get_section(Message.SECTION_ANSWER)[0]#answer_rrset_iter.get_rrset()
  413. self.assertEqual(answer.get_name().to_text(), "example.com.")
  414. self.assertEqual(answer.get_class(), RRClass("IN"))
  415. self.assertEqual(answer.get_type().to_text(), "SOA")
  416. rdata = answer.get_rdata()
  417. self.assertEqual(rdata[0], self.soa_rrset.get_rdata()[0])
  418. # msg is the TSIG_SIGN_EVERY_NTH one
  419. # sending the message with last soa together
  420. self.xfrsess._send_message_with_last_soa(msg, self.sock,
  421. self.soa_rrset, 0,
  422. TSIG_SIGN_EVERY_NTH)
  423. get_msg = self.sock.read_msg()
  424. # tsig context is not exist
  425. self.assertFalse(self.message_has_tsig(get_msg))
  426. def test_send_message_with_last_soa_with_tsig(self):
  427. # create tsig context
  428. self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
  429. msg = self.getmsg()
  430. msg.make_response()
  431. # packet number less than TSIG_SIGN_EVERY_NTH
  432. packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
  433. # msg is not the TSIG_SIGN_EVERY_NTH one
  434. # sending the message with last soa together
  435. self.xfrsess._send_message_with_last_soa(msg, self.sock,
  436. self.soa_rrset, 0,
  437. packet_neet_not_sign)
  438. get_msg = self.sock.read_msg()
  439. self.assertTrue(self.message_has_tsig(get_msg))
  440. self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
  441. self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
  442. self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
  443. # msg is the TSIG_SIGN_EVERY_NTH one
  444. # sending the message with last soa together
  445. self.xfrsess._send_message_with_last_soa(msg, self.sock,
  446. self.soa_rrset, 0,
  447. TSIG_SIGN_EVERY_NTH)
  448. get_msg = self.sock.read_msg()
  449. self.assertTrue(self.message_has_tsig(get_msg))
  450. def test_trigger_send_message_with_last_soa(self):
  451. rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
  452. rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
  453. msg = self.getmsg()
  454. msg.make_response()
  455. msg.add_rrset(Message.SECTION_ANSWER, rrset_a)
  456. # length larger than MAX-len(rrset)
  457. length_need_split = xfrout.XFROUT_MAX_MESSAGE_SIZE - \
  458. get_rrset_len(self.soa_rrset) + 1
  459. # packet number less than TSIG_SIGN_EVERY_NTH
  460. packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
  461. # give the function a value that is larger than MAX-len(rrset)
  462. # this should have triggered the sending of two messages
  463. # (1 with the rrset we added manually, and 1 that triggered
  464. # the sending in _with_last_soa)
  465. self.xfrsess._send_message_with_last_soa(msg, self.sock,
  466. self.soa_rrset,
  467. length_need_split,
  468. packet_neet_not_sign)
  469. get_msg = self.sock.read_msg()
  470. self.assertFalse(self.message_has_tsig(get_msg))
  471. self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
  472. self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
  473. self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
  474. answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
  475. self.assertEqual(answer.get_name().to_text(), "example.com.")
  476. self.assertEqual(answer.get_class(), RRClass("IN"))
  477. self.assertEqual(answer.get_type().to_text(), "A")
  478. rdata = answer.get_rdata()
  479. self.assertEqual(rdata[0].to_text(), "192.0.2.1")
  480. get_msg = self.sock.read_msg()
  481. self.assertFalse(self.message_has_tsig(get_msg))
  482. self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 0)
  483. self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
  484. self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
  485. #answer_rrset_iter = section_iter(get_msg, Message.SECTION_ANSWER)
  486. answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
  487. self.assertEqual(answer.get_name().to_text(), "example.com.")
  488. self.assertEqual(answer.get_class(), RRClass("IN"))
  489. self.assertEqual(answer.get_type().to_text(), "SOA")
  490. rdata = answer.get_rdata()
  491. self.assertEqual(rdata[0], self.soa_rrset.get_rdata()[0])
  492. # and it should not have sent anything else
  493. self.assertEqual(0, len(self.sock.sendqueue))
  494. def test_trigger_send_message_with_last_soa_with_tsig(self):
  495. self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
  496. msg = self.getmsg()
  497. msg.make_response()
  498. msg.add_rrset(Message.SECTION_ANSWER, self.soa_rrset)
  499. # length larger than MAX-len(rrset)
  500. length_need_split = xfrout.XFROUT_MAX_MESSAGE_SIZE - \
  501. get_rrset_len(self.soa_rrset) + 1
  502. # packet number less than TSIG_SIGN_EVERY_NTH
  503. packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
  504. # give the function a value that is larger than MAX-len(rrset)
  505. # this should have triggered the sending of two messages
  506. # (1 with the rrset we added manually, and 1 that triggered
  507. # the sending in _with_last_soa)
  508. self.xfrsess._send_message_with_last_soa(msg, self.sock,
  509. self.soa_rrset,
  510. length_need_split,
  511. packet_neet_not_sign)
  512. get_msg = self.sock.read_msg()
  513. # msg is not the TSIG_SIGN_EVERY_NTH one, it shouldn't be tsig signed
  514. self.assertFalse(self.message_has_tsig(get_msg))
  515. # the last packet should be tsig signed
  516. get_msg = self.sock.read_msg()
  517. self.assertTrue(self.message_has_tsig(get_msg))
  518. # and it should not have sent anything else
  519. self.assertEqual(0, len(self.sock.sendqueue))
  520. # msg is the TSIG_SIGN_EVERY_NTH one, it should be tsig signed
  521. self.xfrsess._send_message_with_last_soa(msg, self.sock,
  522. self.soa_rrset,
  523. length_need_split,
  524. xfrout.TSIG_SIGN_EVERY_NTH)
  525. get_msg = self.sock.read_msg()
  526. self.assertTrue(self.message_has_tsig(get_msg))
  527. # the last packet should be tsig signed
  528. get_msg = self.sock.read_msg()
  529. self.assertTrue(self.message_has_tsig(get_msg))
  530. # and it should not have sent anything else
  531. self.assertEqual(0, len(self.sock.sendqueue))
  532. def test_get_rrset_len(self):
  533. self.assertEqual(82, get_rrset_len(self.soa_rrset))
  534. def test_check_xfrout_available(self):
  535. self.xfrsess.ClientClass = MockDataSrcClient
  536. self.assertEqual(self.xfrsess._check_xfrout_available(
  537. Name('notauth.example.com')), Rcode.NOTAUTH())
  538. self.assertEqual(self.xfrsess._check_xfrout_available(
  539. Name('nosoa.example.com')), Rcode.SERVFAIL())
  540. self.assertEqual(self.xfrsess._check_xfrout_available(
  541. Name('multisoa.example.com')), Rcode.SERVFAIL())
  542. def test_dns_xfrout_start_formerror(self):
  543. # formerror
  544. self.xfrsess.dns_xfrout_start(self.sock, b"\xd6=\x00\x00\x00\x01\x00")
  545. sent_data = self.sock.readsent()
  546. self.assertEqual(len(sent_data), 0)
  547. def default(self, param):
  548. return "example.com"
  549. def test_dns_xfrout_start_notauth(self):
  550. self.xfrsess._get_query_zone_name = self.default
  551. def notauth(formpara):
  552. return Rcode.NOTAUTH()
  553. self.xfrsess._check_xfrout_available = notauth
  554. self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
  555. get_msg = self.sock.read_msg()
  556. self.assertEqual(get_msg.get_rcode().to_text(), "NOTAUTH")
  557. def test_dns_xfrout_start_noerror(self):
  558. self.xfrsess._get_query_zone_name = self.default
  559. def noerror(form):
  560. return Rcode.NOERROR()
  561. self.xfrsess._check_xfrout_available = noerror
  562. def myreply(msg, sock):
  563. self.sock.send(b"success")
  564. self.xfrsess._reply_xfrout_query = myreply
  565. self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
  566. self.assertEqual(self.sock.readsent(), b"success")
  567. def test_reply_xfrout_query_noerror(self):
  568. self.xfrsess._soa = self.soa_rrset
  569. self.xfrsess._iterator = [self.soa_rrset]
  570. self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock)
  571. reply_msg = self.sock.read_msg()
  572. self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 2)
  573. def test_reply_xfrout_query_noerror_with_tsig(self):
  574. rrset = RRset(Name('a.example.com'), RRClass.IN(), RRType.A(),
  575. RRTTL(3600))
  576. rrset.add_rdata(Rdata(RRType.A(), RRClass.IN(), '192.0.2.1'))
  577. global xfrout
  578. def get_rrset_len(rrset):
  579. return 65520
  580. self.xfrsess._soa = self.soa_rrset
  581. self.xfrsess._iterator = [rrset for i in range(0, 100)]
  582. xfrout.get_rrset_len = get_rrset_len
  583. self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
  584. self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock)
  585. # tsig signed first package
  586. reply_msg = self.sock.read_msg()
  587. self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 1)
  588. self.assertTrue(self.message_has_tsig(reply_msg))
  589. # (TSIG_SIGN_EVERY_NTH - 1) packets have no tsig
  590. for i in range(0, xfrout.TSIG_SIGN_EVERY_NTH - 1):
  591. reply_msg = self.sock.read_msg()
  592. self.assertFalse(self.message_has_tsig(reply_msg))
  593. # TSIG_SIGN_EVERY_NTH packet has tsig
  594. reply_msg = self.sock.read_msg()
  595. self.assertTrue(self.message_has_tsig(reply_msg))
  596. for i in range(0, 100 - TSIG_SIGN_EVERY_NTH):
  597. reply_msg = self.sock.read_msg()
  598. self.assertFalse(self.message_has_tsig(reply_msg))
  599. # tsig signed last package
  600. reply_msg = self.sock.read_msg()
  601. self.assertTrue(self.message_has_tsig(reply_msg))
  602. # and it should not have sent anything else
  603. self.assertEqual(0, len(self.sock.sendqueue))
  604. class MyUnixSockServer(UnixSockServer):
  605. def __init__(self):
  606. self._shutdown_event = threading.Event()
  607. self._common_init()
  608. self._cc = MyCCSession()
  609. self.update_config_data(self._cc.get_full_config())
  610. class TestUnixSockServer(unittest.TestCase):
  611. def setUp(self):
  612. self.write_sock, self.read_sock = socket.socketpair()
  613. self.unix = MyUnixSockServer()
  614. def test_guess_remote(self):
  615. """Test we can guess the remote endpoint when we have only the
  616. file descriptor. This is needed, because we get only that one
  617. from auth."""
  618. # We test with UDP, as it can be "connected" without other
  619. # endpoint. Note that in the current implementation _guess_remote()
  620. # unconditionally returns SOCK_STREAM.
  621. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  622. sock.connect(('127.0.0.1', 12345))
  623. self.assertEqual((socket.AF_INET, socket.SOCK_STREAM,
  624. ('127.0.0.1', 12345)),
  625. self.unix._guess_remote(sock.fileno()))
  626. if socket.has_ipv6:
  627. # Don't check IPv6 address on hosts not supporting them
  628. sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
  629. sock.connect(('::1', 12345))
  630. self.assertEqual((socket.AF_INET6, socket.SOCK_STREAM,
  631. ('::1', 12345, 0, 0)),
  632. self.unix._guess_remote(sock.fileno()))
  633. # Try when pretending there's no IPv6 support
  634. # (No need to pretend when there's really no IPv6)
  635. xfrout.socket.has_ipv6 = False
  636. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  637. sock.connect(('127.0.0.1', 12345))
  638. self.assertEqual((socket.AF_INET, socket.SOCK_STREAM,
  639. ('127.0.0.1', 12345)),
  640. self.unix._guess_remote(sock.fileno()))
  641. # Return it back
  642. xfrout.socket.has_ipv6 = True
  643. def test_receive_query_message(self):
  644. send_msg = b"\xd6=\x00\x00\x00\x01\x00"
  645. msg_len = struct.pack('H', socket.htons(len(send_msg)))
  646. self.write_sock.send(msg_len)
  647. self.write_sock.send(send_msg)
  648. recv_msg = self.unix._receive_query_message(self.read_sock)
  649. self.assertEqual(recv_msg, send_msg)
  650. def check_default_ACL(self):
  651. context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1",
  652. 1234, 0, socket.SOCK_DGRAM,
  653. socket.IPPROTO_UDP,
  654. socket.AI_NUMERICHOST)[0][4])
  655. self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context))
  656. def check_loaded_ACL(self, acl):
  657. context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1",
  658. 1234, 0, socket.SOCK_DGRAM,
  659. socket.IPPROTO_UDP,
  660. socket.AI_NUMERICHOST)[0][4])
  661. self.assertEqual(isc.acl.acl.ACCEPT, acl.execute(context))
  662. context = isc.acl.dns.RequestContext(socket.getaddrinfo("192.0.2.1",
  663. 1234, 0, socket.SOCK_DGRAM,
  664. socket.IPPROTO_UDP,
  665. socket.AI_NUMERICHOST)[0][4])
  666. self.assertEqual(isc.acl.acl.REJECT, acl.execute(context))
  667. def test_update_config_data(self):
  668. self.check_default_ACL()
  669. tsig_key_str = 'example.com:SFuWd/q99SzF8Yzd1QbB9g=='
  670. tsig_key_list = [tsig_key_str]
  671. bad_key_list = ['bad..example.com:SFuWd/q99SzF8Yzd1QbB9g==']
  672. self.unix.update_config_data({'transfers_out':10 })
  673. self.assertEqual(self.unix._max_transfers_out, 10)
  674. self.assertTrue(self.unix.tsig_key_ring is not None)
  675. self.check_default_ACL()
  676. self.unix.update_config_data({'transfers_out':9,
  677. 'tsig_key_ring':tsig_key_list})
  678. self.assertEqual(self.unix._max_transfers_out, 9)
  679. self.assertEqual(self.unix.tsig_key_ring.size(), 1)
  680. self.unix.tsig_key_ring.remove(Name("example.com."))
  681. self.assertEqual(self.unix.tsig_key_ring.size(), 0)
  682. # bad tsig key
  683. config_data = {'transfers_out':9, 'tsig_key_ring': bad_key_list}
  684. self.assertRaises(None, self.unix.update_config_data(config_data))
  685. self.assertEqual(self.unix.tsig_key_ring.size(), 0)
  686. # Load the ACL
  687. self.unix.update_config_data({'transfer_acl': [{'from': '127.0.0.1',
  688. 'action': 'ACCEPT'}]})
  689. self.check_loaded_ACL(self.unix._acl)
  690. # Pass a wrong data there and check it does not replace the old one
  691. self.assertRaises(XfroutConfigError,
  692. self.unix.update_config_data,
  693. {'transfer_acl': ['Something bad']})
  694. self.check_loaded_ACL(self.unix._acl)
  695. def test_zone_config_data(self):
  696. # By default, there's no specific zone config
  697. self.assertEqual({}, self.unix._zone_config)
  698. # Adding config for a specific zone. The config is empty unless
  699. # explicitly specified.
  700. self.unix.update_config_data({'zone_config':
  701. [{'origin': 'example.com',
  702. 'class': 'IN'}]})
  703. self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
  704. # zone class can be omitted
  705. self.unix.update_config_data({'zone_config':
  706. [{'origin': 'example.com'}]})
  707. self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
  708. # zone class, name are stored in the "normalized" form. class
  709. # strings are upper cased, names are down cased.
  710. self.unix.update_config_data({'zone_config':
  711. [{'origin': 'EXAMPLE.com'}]})
  712. self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
  713. # invalid zone class, name will result in exceptions
  714. self.assertRaises(EmptyLabel,
  715. self.unix.update_config_data,
  716. {'zone_config': [{'origin': 'bad..example'}]})
  717. self.assertRaises(InvalidRRClass,
  718. self.unix.update_config_data,
  719. {'zone_config': [{'origin': 'example.com',
  720. 'class': 'badclass'}]})
  721. # Configuring a couple of more zones
  722. self.unix.update_config_data({'zone_config':
  723. [{'origin': 'example.com'},
  724. {'origin': 'example.com',
  725. 'class': 'CH'},
  726. {'origin': 'example.org'}]})
  727. self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
  728. self.assertEqual({}, self.unix._zone_config[('CH', 'example.com.')])
  729. self.assertEqual({}, self.unix._zone_config[('IN', 'example.org.')])
  730. # Duplicate data: should be rejected with an exception
  731. self.assertRaises(XfroutConfigError,
  732. self.unix.update_config_data,
  733. {'zone_config': [{'origin': 'example.com'},
  734. {'origin': 'example.org'},
  735. {'origin': 'example.com'}]})
  736. def test_zone_config_data_with_acl(self):
  737. # Similar to the previous test, but with transfer_acl config
  738. self.unix.update_config_data({'zone_config':
  739. [{'origin': 'example.com',
  740. 'transfer_acl':
  741. [{'from': '127.0.0.1',
  742. 'action': 'ACCEPT'}]}]})
  743. acl = self.unix._zone_config[('IN', 'example.com.')]['transfer_acl']
  744. self.check_loaded_ACL(acl)
  745. # invalid ACL syntax will be rejected with exception
  746. self.assertRaises(XfroutConfigError,
  747. self.unix.update_config_data,
  748. {'zone_config': [{'origin': 'example.com',
  749. 'transfer_acl':
  750. [{'action': 'BADACTION'}]}]})
  751. def test_get_db_file(self):
  752. self.assertEqual(self.unix.get_db_file(), "initdb.file")
  753. def test_increase_transfers_counter(self):
  754. self.unix._max_transfers_out = 10
  755. count = self.unix._transfers_counter
  756. self.assertEqual(self.unix.increase_transfers_counter(), True)
  757. self.assertEqual(count + 1, self.unix._transfers_counter)
  758. self.unix._max_transfers_out = 0
  759. count = self.unix._transfers_counter
  760. self.assertEqual(self.unix.increase_transfers_counter(), False)
  761. self.assertEqual(count, self.unix._transfers_counter)
  762. def test_decrease_transfers_counter(self):
  763. count = self.unix._transfers_counter
  764. self.unix.decrease_transfers_counter()
  765. self.assertEqual(count - 1, self.unix._transfers_counter)
  766. def _remove_file(self, sock_file):
  767. try:
  768. os.remove(sock_file)
  769. except OSError:
  770. pass
  771. def test_sock_file_in_use_file_exist(self):
  772. sock_file = 'temp.sock.file'
  773. self._remove_file(sock_file)
  774. self.assertFalse(self.unix._sock_file_in_use(sock_file))
  775. self.assertFalse(os.path.exists(sock_file))
  776. def test_sock_file_in_use_file_not_exist(self):
  777. self.assertFalse(self.unix._sock_file_in_use('temp.sock.file'))
  778. def _start_unix_sock_server(self, sock_file):
  779. serv = ThreadingUnixStreamServer(sock_file, BaseRequestHandler)
  780. serv_thread = threading.Thread(target=serv.serve_forever)
  781. serv_thread.setDaemon(True)
  782. serv_thread.start()
  783. def test_sock_file_in_use(self):
  784. sock_file = 'temp.sock.file'
  785. self._remove_file(sock_file)
  786. self.assertFalse(self.unix._sock_file_in_use(sock_file))
  787. self._start_unix_sock_server(sock_file)
  788. old_stdout = sys.stdout
  789. sys.stdout = open(os.devnull, 'w')
  790. self.assertTrue(self.unix._sock_file_in_use(sock_file))
  791. sys.stdout = old_stdout
  792. def test_remove_unused_sock_file_in_use(self):
  793. sock_file = 'temp.sock.file'
  794. self._remove_file(sock_file)
  795. self.assertFalse(self.unix._sock_file_in_use(sock_file))
  796. self._start_unix_sock_server(sock_file)
  797. old_stdout = sys.stdout
  798. sys.stdout = open(os.devnull, 'w')
  799. try:
  800. self.unix._remove_unused_sock_file(sock_file)
  801. except SystemExit:
  802. pass
  803. else:
  804. # This should never happen
  805. self.assertTrue(False)
  806. sys.stdout = old_stdout
  807. def test_remove_unused_sock_file_dir(self):
  808. import tempfile
  809. dir_name = tempfile.mkdtemp()
  810. old_stdout = sys.stdout
  811. sys.stdout = open(os.devnull, 'w')
  812. try:
  813. self.unix._remove_unused_sock_file(dir_name)
  814. except SystemExit:
  815. pass
  816. else:
  817. # This should never happen
  818. self.assertTrue(False)
  819. sys.stdout = old_stdout
  820. os.rmdir(dir_name)
  821. class TestInitialization(unittest.TestCase):
  822. def setEnv(self, name, value):
  823. if value is None:
  824. if name in os.environ:
  825. del os.environ[name]
  826. else:
  827. os.environ[name] = value
  828. def setUp(self):
  829. self._oldSocket = os.getenv("BIND10_XFROUT_SOCKET_FILE")
  830. self._oldFromBuild = os.getenv("B10_FROM_BUILD")
  831. def tearDown(self):
  832. self.setEnv("B10_FROM_BUILD", self._oldFromBuild)
  833. self.setEnv("BIND10_XFROUT_SOCKET_FILE", self._oldSocket)
  834. # Make sure even the computed values are back
  835. xfrout.init_paths()
  836. def testNoEnv(self):
  837. self.setEnv("B10_FROM_BUILD", None)
  838. self.setEnv("BIND10_XFROUT_SOCKET_FILE", None)
  839. xfrout.init_paths()
  840. self.assertEqual(xfrout.UNIX_SOCKET_FILE,
  841. "@@LOCALSTATEDIR@@/@PACKAGE_NAME@/auth_xfrout_conn")
  842. def testProvidedSocket(self):
  843. self.setEnv("B10_FROM_BUILD", None)
  844. self.setEnv("BIND10_XFROUT_SOCKET_FILE", "The/Socket/File")
  845. xfrout.init_paths()
  846. self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File")
  847. if __name__== "__main__":
  848. isc.log.resetUnitTestRootLogger()
  849. unittest.main()