xfrin.py.in 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845
  1. #!@PYTHON@
  2. # Copyright (C) 2009-2013 Internet Systems Consortium.
  3. #
  4. # Permission to use, copy, modify, and distribute this software for any
  5. # purpose with or without fee is hereby granted, provided that the above
  6. # copyright notice and this permission notice appear in all copies.
  7. #
  8. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  9. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  10. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  11. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  13. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  14. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  15. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. import sys; sys.path.append ('@@PYTHONPATH@@')
  17. import os
  18. import signal
  19. import isc
  20. import asyncore
  21. import struct
  22. import threading
  23. import socket
  24. import random
  25. import time
  26. from functools import reduce
  27. from optparse import OptionParser, OptionValueError
  28. from isc.config.ccsession import *
  29. from isc.statistics import Counters
  30. from isc.notify import notify_out
  31. import isc.util.process
  32. from isc.util.address_formatter import AddressFormatter
  33. from isc.datasrc import DataSourceClient, ZoneFinder
  34. import isc.net.parse
  35. from isc.xfrin.diff import Diff
  36. from isc.server_common.auth_command import auth_loadzone_command
  37. from isc.server_common.tsig_keyring import init_keyring, get_keyring
  38. from isc.server_common.datasrc_clients_mgr import DataSrcClientsMgr, ConfigError
  39. from isc.log_messages.xfrin_messages import *
  40. from isc.dns import *
  41. isc.log.init("b10-xfrin", buffer=True)
  42. logger = isc.log.Logger("xfrin")
  43. # Pending system-wide debug level definitions, the ones we
  44. # use here are hardcoded for now
  45. DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
  46. DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
  47. isc.util.process.rename()
  48. # If B10_FROM_BUILD or B10_FROM_SOURCE is set in the environment, we
  49. # use data files from a directory relative to that, otherwise we use
  50. # the ones installed on the system
  51. SPECFILE_PATH = "@datadir@/@PACKAGE@"\
  52. .replace("${datarootdir}", "@datarootdir@")\
  53. .replace("${prefix}", "@prefix@")
  54. if "B10_FROM_SOURCE" in os.environ:
  55. SPECFILE_PATH = os.environ["B10_FROM_SOURCE"] + "/src/bin/xfrin"
  56. SPECFILE_LOCATION = SPECFILE_PATH + "/xfrin.spec"
  57. AUTH_MODULE_NAME = 'Auth'
  58. XFROUT_MODULE_NAME = 'Xfrout'
  59. # Remote module and identifiers (according to their spec files)
  60. ZONE_MANAGER_MODULE_NAME = 'Zonemgr'
  61. REFRESH_FROM_ZONEMGR = 'refresh_from_zonemgr'
  62. # Constants for debug levels.
  63. DBG_XFRIN_TRACE = logger.DBGLVL_TRACE_BASIC
  64. # These two default are currently hard-coded. For config this isn't
  65. # necessary, but we need these defaults for optional command arguments
  66. # (TODO: have similar support to get default values for command
  67. # arguments as we do for config options)
  68. DEFAULT_MASTER_PORT = 53
  69. DEFAULT_ZONE_CLASS = RRClass.IN
  70. __version__ = 'BIND10'
  71. # Internal result codes of an xfr session
  72. XFRIN_OK = 0 # normal success
  73. XFRIN_FAIL = 1 # general failure (internal/external)
  74. class XfrinException(Exception):
  75. pass
  76. class XfrinProtocolError(Exception):
  77. '''An exception raised for errors encountered in xfrin protocol handling.
  78. '''
  79. pass
  80. class XfrinZoneError(Exception):
  81. '''
  82. An exception raised when the received zone is broken enough to be unusable.
  83. '''
  84. pass
  85. class XfrinZoneUptodate(Exception):
  86. '''
  87. Thrown when the zone is already up to date, so there's no need to download
  88. the zone. This is not really an error case (but it's still an exceptional
  89. condition and the control flow is different than usual).
  90. '''
  91. pass
  92. class XfrinZoneInfoException(Exception):
  93. """This exception is raised if there is an error in the given
  94. configuration (part), or when a command does not have a required
  95. argument or has bad arguments, for instance when the zone's master
  96. address is not a valid IP address, when the zone does not
  97. have a name, or when multiple settings are given for the same
  98. zone."""
  99. pass
  100. def _check_zone_name(zone_name_str):
  101. """Checks if the given zone name is a valid domain name, and returns
  102. it as a Name object. Raises an XfrinException if it is not."""
  103. try:
  104. # In the _zones dict, part of the key is the zone name,
  105. # but due to a limitation in the Name class, we
  106. # cannot directly use it as a dict key, and we use to_text()
  107. #
  108. # Downcase the name here for that reason.
  109. return Name(zone_name_str, True)
  110. except (EmptyLabel, TooLongLabel, BadLabelType, BadEscape,
  111. TooLongName, IncompleteName) as ne:
  112. raise XfrinZoneInfoException("bad zone name: " + zone_name_str + " (" + str(ne) + ")")
  113. def _check_zone_class(zone_class_str):
  114. """If the given argument is a string: checks if the given class is
  115. a valid one, and returns an RRClass object if so.
  116. Raises XfrinZoneInfoException if not.
  117. If it is None, this function returns the default RRClass.IN"""
  118. if zone_class_str is None:
  119. return DEFAULT_ZONE_CLASS
  120. try:
  121. return RRClass(zone_class_str)
  122. except InvalidRRClass as irce:
  123. raise XfrinZoneInfoException("bad zone class: " + zone_class_str + " (" + str(irce) + ")")
  124. def format_zone_str(zone_name, zone_class):
  125. """Helper function to format a zone name and class as a string of
  126. the form '<name>/<class>'.
  127. Parameters:
  128. zone_name (isc.dns.Name) name to format
  129. zone_class (isc.dns.RRClass) class to format
  130. """
  131. return zone_name.to_text(True) + '/' + str(zone_class)
  132. def format_addrinfo(addrinfo):
  133. """Helper function to format the addrinfo as a string of the form
  134. <addr>:<port> (for IPv4) or [<addr>]:port (for IPv6). For unix domain
  135. sockets, and unknown address families, it returns a basic string
  136. conversion of the third element of the passed tuple.
  137. Parameters:
  138. addrinfo: a 3-tuple consisting of address family, socket type, and,
  139. depending on the family, either a 2-tuple with the address
  140. and port, or a filename
  141. """
  142. try:
  143. if addrinfo[0] == socket.AF_INET:
  144. return str(addrinfo[2][0]) + ":" + str(addrinfo[2][1])
  145. elif addrinfo[0] == socket.AF_INET6:
  146. return "[" + str(addrinfo[2][0]) + "]:" + str(addrinfo[2][1])
  147. else:
  148. return str(addrinfo[2])
  149. except IndexError:
  150. raise TypeError("addrinfo argument to format_addrinfo() does not "
  151. "appear to be consisting of (family, socktype, (addr, port))")
  152. def get_soa_serial(soa_rdata):
  153. '''Extract the serial field of SOA RDATA and return it as a Serial object.
  154. We don't have to be very efficient here, so we first dump the entire RDATA
  155. as a string and convert the first corresponding field. This should be
  156. sufficient in practice, but may not always work when the MNAME or RNAME
  157. contains an (escaped) space character in their labels. Ideally there
  158. should be a more direct and convenient way to get access to the SOA
  159. fields.
  160. '''
  161. return Serial(int(soa_rdata.to_text().split()[2]))
  162. class XfrinState:
  163. '''
  164. The states of the incoming *XFR state machine.
  165. We (will) handle both IXFR and AXFR with a single integrated state
  166. machine because they cannot be distinguished immediately - an AXFR
  167. response to an IXFR request can only be detected when the first two (2)
  168. response RRs have already been received.
  169. The following diagram summarizes the state transition. After sending
  170. the query, xfrin starts the process with the InitialSOA state (all
  171. IXFR/AXFR response begins with an SOA). When it reaches IXFREnd
  172. or AXFREnd, the process successfully completes.
  173. (AXFR or
  174. (recv SOA) AXFR-style IXFR) (SOA, add)
  175. InitialSOA------->FirstData------------->AXFR--------->AXFREnd
  176. | | | ^ (post xfr
  177. |(IXFR && | | | checks, then
  178. | recv SOA | +--+ commit)
  179. | not new) | (non SOA, add)
  180. V |
  181. IXFRUptodate | (non SOA, delete)
  182. (pure IXFR,| +-------+
  183. keep handling)| (Delete SOA) V |
  184. + ->IXFRDeleteSOA------>IXFRDelete--+
  185. ^ |
  186. (see SOA, not end, | (see SOA)|
  187. commit, keep handling) | |
  188. | V
  189. +---------IXFRAdd<----------+IXFRAddSOA
  190. (non SOA, add)| ^ | (Add SOA)
  191. ----------+ |
  192. |(see SOA w/ end serial, commit changes)
  193. V
  194. IXFREnd
  195. Note that changes are committed for every "difference sequence"
  196. (i.e. changes for one SOA update). This means when an IXFR response
  197. contains multiple difference sequences and something goes wrong
  198. after several commits, these changes have been published and visible
  199. to clients even if the IXFR session is subsequently aborted.
  200. It is not clear if this is valid in terms of the protocol specification.
  201. Section 4 of RFC 1995 states:
  202. An IXFR client, should only replace an older version with a newer
  203. version after all the differences have been successfully processed.
  204. If this "replacement" is for the changes of one difference sequence
  205. and "all the differences" mean the changes for that sequence, this
  206. implementation strictly follows what RFC states. If this is for
  207. the entire IXFR response (that may contain multiple sequences),
  208. we should implement it with one big transaction and one final commit
  209. at the very end.
  210. For now, we implement it with multiple smaller commits for two
  211. reasons. First, this is what BIND 9 does, and we generally port
  212. the implementation logic here. BIND 9 has been supporting IXFR
  213. for many years, so the fact that it still behaves this way
  214. probably means it at least doesn't cause a severe operational
  215. problem in practice. Second, especially because BIND 10 would
  216. often uses a database backend, a larger transaction could cause an
  217. undesirable effects, e.g. suspending normal lookups for a longer
  218. period depending on the characteristics of the database. Even if
  219. we find something wrong in a later sequeunce and abort the
  220. session, we can start another incremental update from what has
  221. been validated, or we can switch to AXFR to replace the zone
  222. completely.
  223. This implementation uses the state design pattern, where each state
  224. is represented as a subclass of the base XfrinState class. Each concrete
  225. subclass of XfrinState is assumed to define two methods: handle_rr() and
  226. finish_message(). These methods handle specific part of XFR protocols
  227. and (if necessary) perform the state transition.
  228. Conceptually, XfrinState and its subclasses are a "friend" of
  229. XfrinConnection and are assumed to be allowed to access its internal
  230. information (even though Python does not have a strict access control
  231. between different classes).
  232. The XfrinState and its subclasses are designed to be stateless, and
  233. can be used as singleton objects. For now, however, we always instantiate
  234. a new object for every state transition, partly because the introduction
  235. of singleton will make a code bit complicated, and partly because
  236. the overhead of object instantiation wouldn't be significant for xfrin.
  237. '''
  238. def set_xfrstate(self, conn, new_state):
  239. '''Set the XfrConnection to a given new state.
  240. As a "friend" class, this method intentionally gets access to the
  241. connection's "private" method.
  242. '''
  243. conn._XfrinConnection__set_xfrstate(new_state)
  244. def handle_rr(self, conn):
  245. '''Handle one RR of an XFR response message.
  246. Depending on the state, the RR is generally added or deleted in the
  247. corresponding data source, or in some special cases indicates
  248. a specifi transition, such as starting a new IXFR difference
  249. sequence or completing the session.
  250. All subclass has their specific behaviors for this method, so
  251. there is no default definition. If the base class version
  252. is called, it's a bug of the caller, and it's notified via
  253. an XfrinException exception.
  254. This method returns a boolean value: True if the given RR was
  255. fully handled and the caller should go to the next RR; False
  256. if the caller needs to call this method with the (possibly) new
  257. state for the same RR again.
  258. '''
  259. raise XfrinException("Internal bug: " +
  260. "XfrinState.handle_rr() called directly")
  261. def finish_message(self, conn):
  262. '''Perform any final processing after handling all RRs of a response.
  263. This method then returns a boolean indicating whether to continue
  264. receiving the message. Unless it's in the end of the entire XFR
  265. session, we should continue, so this default method simply returns
  266. True.
  267. '''
  268. return True
  269. class XfrinInitialSOA(XfrinState):
  270. def handle_rr(self, conn, rr):
  271. if rr.get_type() != RRType.SOA:
  272. raise XfrinProtocolError('First RR in zone transfer must be SOA ('
  273. + rr.get_type().to_text() + ' received)')
  274. conn._end_serial = get_soa_serial(rr.get_rdata()[0])
  275. if conn._request_type == RRType.IXFR and \
  276. conn._end_serial <= conn._request_serial:
  277. logger.info(XFRIN_IXFR_UPTODATE, conn.zone_str(),
  278. conn._request_serial, conn._end_serial)
  279. self.set_xfrstate(conn, XfrinIXFRUptodate())
  280. else:
  281. self.set_xfrstate(conn, XfrinFirstData())
  282. return True
  283. class XfrinFirstData(XfrinState):
  284. def handle_rr(self, conn, rr):
  285. '''Handle the first RR after initial SOA in an XFR session.
  286. This state happens exactly once in an XFR session, where
  287. we decide whether it's incremental update ("real" IXFR) or
  288. non incremental update (AXFR or AXFR-style IXFR).
  289. If we initiated IXFR and the transfer begins with two SOAs
  290. (the serial of the second one being equal to our serial),
  291. it's incremental; otherwise it's non incremental.
  292. This method always return False (unlike many other handle_rr()
  293. methods) because this first RR must be examined again in the
  294. determined update context.
  295. Note that in the non incremental case the RR should normally be
  296. something other SOA, but it's still possible it's an SOA with a
  297. different serial than ours. The only possible interpretation at
  298. this point is that it's non incremental update that only consists
  299. of the SOA RR. It will result in broken zone (for example, it
  300. wouldn't even contain an apex NS) and should be rejected at post
  301. XFR processing, but in terms of the XFR session processing we
  302. accept it and move forward.
  303. Note further that, in the half-broken SOA-only transfer case,
  304. these two SOAs are supposed to be the same as stated in Section 2.2
  305. of RFC 5936. We don't check that condition here, either; we'll
  306. leave whether and how to deal with that situation to the end of
  307. the processing of non incremental update. See also a related
  308. discussion at the IETF dnsext wg:
  309. http://www.ietf.org/mail-archive/web/dnsext/current/msg07908.html
  310. '''
  311. if conn._request_type == RRType.IXFR and \
  312. rr.get_type() == RRType.SOA and \
  313. conn._request_serial == get_soa_serial(rr.get_rdata()[0]):
  314. logger.debug(DBG_XFRIN_TRACE, XFRIN_GOT_INCREMENTAL_RESP,
  315. conn.zone_str())
  316. conn._diff = None # Will be created on-demand
  317. self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
  318. else:
  319. logger.debug(DBG_XFRIN_TRACE, XFRIN_GOT_NONINCREMENTAL_RESP,
  320. conn.zone_str())
  321. # We are now going to add RRs to the new zone. We need create
  322. # a Diff object. It will be used throughtout the XFR session.
  323. conn._diff = Diff(conn._datasrc_client, conn._zone_name, True)
  324. self.set_xfrstate(conn, XfrinAXFR())
  325. return False
  326. class XfrinIXFRDeleteSOA(XfrinState):
  327. def handle_rr(self, conn, rr):
  328. if rr.get_type() != RRType.SOA:
  329. # this shouldn't happen; should this occur it means an internal
  330. # bug.
  331. raise XfrinException(rr.get_type().to_text() +
  332. ' RR is given in IXFRDeleteSOA state')
  333. # This is the beginning state of one difference sequence (changes
  334. # for one SOA update). We may need to create a new Diff object now.
  335. # Note also that we (unconditionally) enable journaling here. The
  336. # Diff constructor may internally disable it, however, if the
  337. # underlying data source doesn't support journaling.
  338. if conn._diff is None:
  339. conn._diff = Diff(conn._datasrc_client, conn._zone_name, False,
  340. True)
  341. conn._diff.delete_data(rr)
  342. self.set_xfrstate(conn, XfrinIXFRDelete())
  343. conn.get_transfer_stats().ixfr_deletion_count += 1
  344. return True
  345. class XfrinIXFRDelete(XfrinState):
  346. def handle_rr(self, conn, rr):
  347. if rr.get_type() == RRType.SOA:
  348. # This is the only place where current_serial is set
  349. conn._current_serial = get_soa_serial(rr.get_rdata()[0])
  350. self.set_xfrstate(conn, XfrinIXFRAddSOA())
  351. return False
  352. conn._diff.delete_data(rr)
  353. conn.get_transfer_stats().ixfr_deletion_count += 1
  354. return True
  355. class XfrinIXFRAddSOA(XfrinState):
  356. def handle_rr(self, conn, rr):
  357. if rr.get_type() != RRType.SOA:
  358. # this shouldn't happen; should this occur it means an internal
  359. # bug.
  360. raise XfrinException(rr.get_type().to_text() +
  361. ' RR is given in IXFRAddSOA state')
  362. conn._diff.add_data(rr)
  363. self.set_xfrstate(conn, XfrinIXFRAdd())
  364. conn.get_transfer_stats().ixfr_addition_count += 1
  365. return True
  366. class XfrinIXFRAdd(XfrinState):
  367. def handle_rr(self, conn, rr):
  368. if rr.get_type() == RRType.SOA:
  369. # This SOA marks the end of a difference sequence
  370. conn.get_transfer_stats().ixfr_changeset_count += 1
  371. soa_serial = get_soa_serial(rr.get_rdata()[0])
  372. if soa_serial == conn._end_serial:
  373. # The final part is there. Finish the transfer by
  374. # checking the last TSIG (if required), the zone data and
  375. # committing.
  376. conn.finish_transfer()
  377. self.set_xfrstate(conn, XfrinIXFREnd())
  378. return True
  379. elif soa_serial != conn._current_serial:
  380. raise XfrinProtocolError('IXFR out of sync: expected ' +
  381. 'serial ' +
  382. str(conn._current_serial) +
  383. ', got ' + str(soa_serial))
  384. else:
  385. # Apply a change to the database. But don't commit it yet,
  386. # we can't know if the message is/will be properly signed.
  387. # A complete commit will happen after the last bit.
  388. conn._diff.apply()
  389. self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
  390. return False
  391. conn._diff.add_data(rr)
  392. conn.get_transfer_stats().ixfr_addition_count += 1
  393. return True
  394. class XfrinIXFREnd(XfrinState):
  395. def handle_rr(self, conn, rr):
  396. raise XfrinProtocolError('Extra data after the end of IXFR diffs: ' +
  397. rr.to_text())
  398. def finish_message(self, conn):
  399. '''Final processing after processing an entire IXFR session.
  400. There will be more actions here, but for now we simply return False,
  401. indicating there will be no more message to receive.
  402. '''
  403. return False
  404. class XfrinIXFRUptodate(XfrinState):
  405. def handle_rr(self, conn, rr):
  406. raise XfrinProtocolError('Extra data after single IXFR response ' +
  407. rr.to_text())
  408. def finish_message(self, conn):
  409. raise XfrinZoneUptodate
  410. class XfrinAXFR(XfrinState):
  411. def handle_rr(self, conn, rr):
  412. """
  413. Handle the RR by putting it into the zone.
  414. """
  415. conn._diff.add_data(rr)
  416. if rr.get_type() == RRType.SOA:
  417. # SOA means end. Don't commit it yet - we need to perform
  418. # post-transfer checks
  419. soa_serial = get_soa_serial(rr.get_rdata()[0])
  420. if conn._end_serial != soa_serial:
  421. logger.warn(XFRIN_AXFR_INCONSISTENT_SOA, conn.zone_str(),
  422. conn._end_serial, soa_serial)
  423. self.set_xfrstate(conn, XfrinAXFREnd())
  424. conn.get_transfer_stats().axfr_rr_count += 1
  425. # Yes, we've eaten this RR.
  426. return True
  427. class XfrinAXFREnd(XfrinState):
  428. def handle_rr(self, conn, rr):
  429. raise XfrinProtocolError('Extra data after the end of AXFR: ' +
  430. rr.to_text())
  431. def finish_message(self, conn):
  432. """
  433. Final processing after processing an entire AXFR session.
  434. This simply calls the finish_transfer method of the connection
  435. that ensures it is signed by TSIG (if required), the zone data
  436. is valid and commits it.
  437. """
  438. conn.finish_transfer()
  439. return False
  440. class XfrinTransferStats:
  441. """
  442. This class keeps a record of transfer data for logging purposes.
  443. It records number of messages, rrs, and bytes transfered, as well
  444. as the start and end time. The start time is set upon instantiation of
  445. this class. The end time is set the first time finalize(),
  446. get_running_time(), or get_bytes_per_second() is called. The end time is
  447. set only once; subsequent calls to any of these methods does not modify
  448. it further.
  449. All _count instance variables can be directly set as needed by the
  450. class collecting these results.
  451. """
  452. def __init__(self):
  453. self.message_count = 0
  454. self.axfr_rr_count = 0
  455. self.byte_count = 0
  456. self.ixfr_changeset_count = 0;
  457. self.ixfr_deletion_count = 0;
  458. self.ixfr_addition_count = 0;
  459. self._start_time = time.time()
  460. self._end_time = None
  461. def finalize(self):
  462. """Sets the end time to time.time() if not done already."""
  463. if self._end_time is None:
  464. self._end_time = time.time()
  465. def get_running_time(self):
  466. """Calls finalize(), then returns the difference between creation
  467. and finalization time"""
  468. self.finalize()
  469. return self._end_time - self._start_time
  470. def get_bytes_per_second(self):
  471. """Returns the number of bytes per second, based on the result of
  472. get_running_time() and the value of bytes_count."""
  473. runtime = self.get_running_time()
  474. if runtime > 0.0:
  475. return float(self.byte_count) / runtime
  476. else:
  477. # This should never happen, but if some clock is so
  478. # off or reset in the meantime, we do need to return
  479. # *something* (and not raise an error)
  480. if self.byte_count == 0:
  481. return 0.0
  482. else:
  483. return float("inf")
  484. class XfrinConnection(asyncore.dispatcher):
  485. '''Do xfrin in this class. '''
  486. def __init__(self,
  487. sock_map, zone_name, rrclass, datasrc_client,
  488. shutdown_event, master_addrinfo, zone_soa, tsig_key=None,
  489. idle_timeout=60):
  490. """Constructor of the XfirnConnection class.
  491. Parameters:
  492. sock_map: empty dict, used with asyncore.
  493. zone_name (dns.Name): Zone name.
  494. rrclass (dns.RRClass): Zone RR class.
  495. datasrc_client (DataSourceClient): the data source client object
  496. used for the XFR session.
  497. shutdown_event (threading.Event): used for synchronization with
  498. parent thread.
  499. master_addrinfo (tuple: (sock family, sock type, sockaddr)):
  500. address and port of the master server.
  501. zone_soa (RRset or None): SOA RRset of zone's current SOA or None
  502. if it's not available.
  503. idle_timeout (int): max idle time for read data from socket.
  504. """
  505. asyncore.dispatcher.__init__(self, map=sock_map)
  506. # The XFR state. Conceptually this is purely private, so we emphasize
  507. # the fact by the double underscore. Other classes are assumed to
  508. # get access to this via get_xfrstate(), and only XfrinState classes
  509. # are assumed to be allowed to modify it via __set_xfrstate().
  510. self.__state = None
  511. # Requested transfer type (RRType.AXFR or RRType.IXFR). The actual
  512. # transfer type may differ due to IXFR->AXFR fallback:
  513. self._request_type = None
  514. # Zone parameters
  515. self._zone_name = zone_name
  516. self._rrclass = rrclass
  517. # Data source handler
  518. self._datasrc_client = datasrc_client
  519. self._zone_soa = zone_soa
  520. self._sock_map = sock_map
  521. self._soa_rr_count = 0
  522. self._idle_timeout = idle_timeout
  523. self._shutdown_event = shutdown_event
  524. self._master_addrinfo = master_addrinfo
  525. self._tsig_key = tsig_key
  526. self._tsig_ctx = None
  527. # tsig_ctx_creator is introduced to allow tests to use a mock class for
  528. # easier tests (in normal case we always use the default)
  529. self._tsig_ctx_creator = lambda key : TSIGContext(key)
  530. # keep a record of this specific transfer to log on success
  531. # (time, rr/s, etc)
  532. self._transfer_stats = XfrinTransferStats()
  533. self._counters = Counters(SPECFILE_LOCATION)
  534. def init_socket(self):
  535. '''Initialize the underlyig socket.
  536. This is essentially a part of __init__() and is expected to be
  537. called immediately after the constructor. It's separated from
  538. the constructor because otherwise we might not be able to close
  539. it if the constructor raises an exception after opening the socket.
  540. '''
  541. self.create_socket(self._master_addrinfo[0], self._master_addrinfo[1])
  542. self.socket.setblocking(1)
  543. def __set_xfrstate(self, new_state):
  544. self.__state = new_state
  545. def get_xfrstate(self):
  546. return self.__state
  547. def get_transfer_stats(self):
  548. """Returns the transfer stats object, used to measure transfer time,
  549. and number of messages/records/bytes transfered."""
  550. return self._transfer_stats
  551. def zone_str(self):
  552. '''A convenience function for logging to include zone name and class'''
  553. return format_zone_str(self._zone_name, self._rrclass)
  554. def connect_to_master(self):
  555. '''Connect to master in TCP.'''
  556. try:
  557. self.connect(self._master_addrinfo[2])
  558. return True
  559. except socket.error as e:
  560. logger.error(XFRIN_CONNECT_MASTER, self._master_addrinfo[2],
  561. str(e))
  562. return False
  563. def _create_query(self, query_type):
  564. '''Create an XFR-related query message.
  565. query_type is either SOA, AXFR or IXFR. An IXFR query needs the
  566. zone's current SOA record. If it's not known, it raises an
  567. XfrinException exception. Note that this may not necessarily a
  568. broken configuration; for the first attempt of transfer the secondary
  569. may not have any boot-strap zone information, in which case IXFR
  570. simply won't work. The xfrin should then fall back to AXFR.
  571. _request_serial is recorded for later use.
  572. '''
  573. msg = Message(Message.RENDER)
  574. query_id = random.randint(0, 0xFFFF)
  575. self._query_id = query_id
  576. msg.set_qid(query_id)
  577. msg.set_opcode(Opcode.QUERY)
  578. msg.set_rcode(Rcode.NOERROR)
  579. msg.add_question(Question(self._zone_name, self._rrclass, query_type))
  580. # Remember our serial, if known
  581. self._request_serial = get_soa_serial(self._zone_soa.get_rdata()[0]) \
  582. if self._zone_soa is not None else None
  583. # Set the authority section with our SOA for IXFR
  584. if query_type == RRType.IXFR:
  585. if self._zone_soa is None:
  586. # (incremental) IXFR doesn't work without known SOA
  587. raise XfrinException('Failed to create IXFR query due to no ' +
  588. 'SOA for ' + self.zone_str())
  589. msg.add_rrset(Message.SECTION_AUTHORITY, self._zone_soa)
  590. return msg
  591. def _send_data(self, data):
  592. size = len(data)
  593. total_count = 0
  594. while total_count < size:
  595. count = self.send(data[total_count:])
  596. total_count += count
  597. def _send_query(self, query_type):
  598. '''Send query message over TCP. '''
  599. msg = self._create_query(query_type)
  600. render = MessageRenderer()
  601. # XXX Currently, python wrapper doesn't accept 'None' parameter in this
  602. # case, we should remove the if statement and use a universal
  603. # interface later.
  604. if self._tsig_key is not None:
  605. self._tsig_ctx = self._tsig_ctx_creator(self._tsig_key)
  606. msg.to_wire(render, self._tsig_ctx)
  607. else:
  608. msg.to_wire(render)
  609. header_len = struct.pack('H', socket.htons(render.get_length()))
  610. self._send_data(header_len)
  611. self._send_data(render.get_data())
  612. def _asyncore_loop(self):
  613. '''
  614. This method is a trivial wrapper for asyncore.loop(). It's extracted from
  615. _get_request_response so that we can test the rest of the code without
  616. involving actual communication with a remote server.'''
  617. asyncore.loop(self._idle_timeout, map=self._sock_map, count=1)
  618. def _get_request_response(self, size):
  619. recv_size = 0
  620. data = b''
  621. while recv_size < size:
  622. self._recv_time_out = True
  623. self._need_recv_size = size - recv_size
  624. self._asyncore_loop()
  625. if self._recv_time_out:
  626. raise XfrinException('receive data from socket time out.')
  627. recv_size += self._recvd_size
  628. data += self._recvd_data
  629. return data
  630. def _check_response_tsig(self, msg, response_data):
  631. tsig_record = msg.get_tsig_record()
  632. if self._tsig_ctx is not None:
  633. tsig_error = self._tsig_ctx.verify(tsig_record, response_data)
  634. if tsig_error != TSIGError.NOERROR:
  635. raise XfrinProtocolError('TSIG verify fail: %s' %
  636. str(tsig_error))
  637. elif tsig_record is not None:
  638. # If the response includes a TSIG while we didn't sign the query,
  639. # we treat it as an error. RFC doesn't say anything about this
  640. # case, but it clearly states the server must not sign a response
  641. # to an unsigned request. Although we could be flexible, no sane
  642. # implementation would return such a response, and since this is
  643. # part of security mechanism, it's probably better to be more
  644. # strict.
  645. raise XfrinProtocolError('Unexpected TSIG in response')
  646. def _check_response_tsig_last(self):
  647. """
  648. Check there's a signature at the last message.
  649. """
  650. if self._tsig_ctx is not None:
  651. if not self._tsig_ctx.last_had_signature():
  652. raise XfrinProtocolError('TSIG verify fail: no TSIG on last '+
  653. 'message')
  654. def __validate_error(self, reason):
  655. '''
  656. Used as error callback below.
  657. '''
  658. logger.error(XFRIN_ZONE_INVALID, self._zone_name, self._rrclass,
  659. reason)
  660. def __validate_warning(self, reason):
  661. '''
  662. Used as warning callback below.
  663. '''
  664. logger.warn(XFRIN_ZONE_WARN, self._zone_name, self._rrclass, reason)
  665. def finish_transfer(self):
  666. """
  667. Perform any necessary checks after a transfer. Then complete the
  668. transfer by committing the transaction into the data source.
  669. """
  670. self._check_response_tsig_last()
  671. if not check_zone(self._zone_name, self._rrclass,
  672. self._diff.get_rrset_collection(),
  673. (self.__validate_error, self.__validate_warning)):
  674. raise XfrinZoneError('Validation of the new zone failed')
  675. self._diff.commit()
  676. def __parse_soa_response(self, msg, response_data):
  677. '''Parse a response to SOA query and extract the SOA from answer.
  678. This is a subroutine of _check_soa_serial(). This method also
  679. validates message, and rejects bogus responses with XfrinProtocolError.
  680. If everything is okay, it returns the SOA RR from the answer section
  681. of the response.
  682. '''
  683. # Check TSIG integrity and validate the header. Unlike AXFR/IXFR,
  684. # we should be more strict for SOA queries and check the AA flag, too.
  685. self._check_response_tsig(msg, response_data)
  686. self._check_response_header(msg)
  687. if not msg.get_header_flag(Message.HEADERFLAG_AA):
  688. raise XfrinProtocolError('non-authoritative answer to SOA query')
  689. # Validate the question section
  690. n_question = msg.get_rr_count(Message.SECTION_QUESTION)
  691. if n_question != 1:
  692. raise XfrinProtocolError('Invalid response to SOA query: ' +
  693. '(' + str(n_question) + ' questions, 1 ' +
  694. 'expected)')
  695. resp_question = msg.get_question()[0]
  696. if resp_question.get_name() != self._zone_name or \
  697. resp_question.get_class() != self._rrclass or \
  698. resp_question.get_type() != RRType.SOA:
  699. raise XfrinProtocolError('Invalid response to SOA query: '
  700. 'question mismatch: ' +
  701. str(resp_question))
  702. # Look into the answer section for SOA
  703. soa = None
  704. for rr in msg.get_section(Message.SECTION_ANSWER):
  705. if rr.get_type() == RRType.SOA:
  706. if soa is not None:
  707. raise XfrinProtocolError('SOA response had multiple SOAs')
  708. soa = rr
  709. # There should not be a CNAME record at top of zone.
  710. if rr.get_type() == RRType.CNAME:
  711. raise XfrinProtocolError('SOA query resulted in CNAME')
  712. # If SOA is not found, try to figure out the reason then report it.
  713. if soa is None:
  714. # See if we have any SOA records in the authority section.
  715. for rr in msg.get_section(Message.SECTION_AUTHORITY):
  716. if rr.get_type() == RRType.NS:
  717. raise XfrinProtocolError('SOA query resulted in referral')
  718. if rr.get_type() == RRType.SOA:
  719. raise XfrinProtocolError('SOA query resulted in NODATA')
  720. raise XfrinProtocolError('No SOA record found in response to ' +
  721. 'SOA query')
  722. # Check if the SOA is really what we asked for
  723. if soa.get_name() != self._zone_name or \
  724. soa.get_class() != self._rrclass:
  725. raise XfrinProtocolError("SOA response doesn't match query: " +
  726. str(soa))
  727. # All okay, return it
  728. return soa
  729. def _get_ipver_str(self):
  730. """Returns a 'v4' or 'v6' string representing a IP version
  731. depending on the socket family. This is for an internal use
  732. only (except for tests). This is supported only for IP sockets.
  733. It raises a ValueError exception on other address families.
  734. """
  735. if self.socket.family == socket.AF_INET:
  736. return 'v4'
  737. elif self.socket.family == socket.AF_INET6:
  738. return 'v6'
  739. raise ValueError("Invalid address family. "
  740. "This is supported only for IP sockets")
  741. def _check_soa_serial(self):
  742. '''Send SOA query and compare the local and remote serials.
  743. If we know our local serial and the remote serial isn't newer
  744. than ours, we abort the session with XfrinZoneUptodate.
  745. On success it returns XFRIN_OK for testing. The caller won't use it.
  746. '''
  747. self._send_query(RRType.SOA)
  748. # count soaoutv4 or soaoutv6 requests
  749. self._counters.inc('zones', self._rrclass.to_text(),
  750. self._zone_name.to_text(), 'soaout' +
  751. self._get_ipver_str())
  752. data_len = self._get_request_response(2)
  753. msg_len = socket.htons(struct.unpack('H', data_len)[0])
  754. soa_response = self._get_request_response(msg_len)
  755. msg = Message(Message.PARSE)
  756. msg.from_wire(soa_response, Message.PRESERVE_ORDER)
  757. # Validate/parse the rest of the response, and extract the SOA
  758. # from the answer section
  759. soa = self.__parse_soa_response(msg, soa_response)
  760. # Compare the two serials. If ours is 'new', abort with ZoneUptodate.
  761. primary_serial = get_soa_serial(soa.get_rdata()[0])
  762. if self._request_serial is not None and \
  763. self._request_serial >= primary_serial:
  764. if self._request_serial != primary_serial:
  765. logger.info(XFRIN_ZONE_SERIAL_AHEAD, primary_serial,
  766. self.zone_str(),
  767. format_addrinfo(self._master_addrinfo),
  768. self._request_serial)
  769. raise XfrinZoneUptodate
  770. return XFRIN_OK
  771. def do_xfrin(self, check_soa, request_type=RRType.AXFR):
  772. '''Do an xfr session by sending xfr request and parsing responses.'''
  773. try:
  774. ret = XFRIN_OK
  775. self._request_type = request_type
  776. req_str = request_type.to_text()
  777. if check_soa:
  778. self._check_soa_serial()
  779. self.close()
  780. self.init_socket()
  781. if not self.connect_to_master():
  782. raise XfrinException('Unable to reconnect to master')
  783. # start statistics timer
  784. # Note: If the timer for the zone is already started but
  785. # not yet stopped due to some error, the last start time
  786. # is overwritten at this point.
  787. self._counters.start_timer('zones',
  788. self._rrclass.to_text(),
  789. self._zone_name.to_text(),
  790. 'last_' + req_str.lower() +
  791. '_duration')
  792. logger.info(XFRIN_XFR_TRANSFER_STARTED, req_str, self.zone_str())
  793. # An AXFR or IXFR is being requested.
  794. self._counters.inc('zones', self._rrclass.to_text(),
  795. self._zone_name.to_text(),
  796. req_str.lower() + 'req' +
  797. self._get_ipver_str())
  798. self._send_query(self._request_type)
  799. self.__state = XfrinInitialSOA()
  800. self._handle_xfrin_responses()
  801. # Depending what data was found, we log different status reports
  802. # (In case of an AXFR-style IXFR, print the 'AXFR' message)
  803. if self._transfer_stats.axfr_rr_count == 0:
  804. logger.info(XFRIN_IXFR_TRANSFER_SUCCESS,
  805. self.zone_str(),
  806. self._transfer_stats.message_count,
  807. self._transfer_stats.ixfr_changeset_count,
  808. self._transfer_stats.ixfr_deletion_count,
  809. self._transfer_stats.ixfr_addition_count,
  810. self._transfer_stats.byte_count,
  811. "%.3f" % self._transfer_stats.get_running_time(),
  812. "%.f" % self._transfer_stats.get_bytes_per_second()
  813. )
  814. else:
  815. logger.info(XFRIN_TRANSFER_SUCCESS,
  816. req_str,
  817. self.zone_str(),
  818. self._transfer_stats.message_count,
  819. self._transfer_stats.axfr_rr_count,
  820. self._transfer_stats.byte_count,
  821. "%.3f" % self._transfer_stats.get_running_time(),
  822. "%.f" % self._transfer_stats.get_bytes_per_second()
  823. )
  824. except XfrinZoneUptodate:
  825. # Eventually we'll probably have to treat this case as a trigger
  826. # of trying another primary server, etc, but for now we treat it
  827. # as "success".
  828. pass
  829. except XfrinZoneError:
  830. # The log message doesn't contain the exception text, since there's
  831. # only one place where the exception is thrown now and it'd be the
  832. # same generic message every time.
  833. logger.error(XFRIN_INVALID_ZONE_DATA, self.zone_str(),
  834. format_addrinfo(self._master_addrinfo))
  835. ret = XFRIN_FAIL
  836. except XfrinProtocolError as e:
  837. logger.info(XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION, req_str,
  838. self.zone_str(),
  839. format_addrinfo(self._master_addrinfo), str(e))
  840. ret = XFRIN_FAIL
  841. except XfrinException as e:
  842. logger.error(XFRIN_XFR_TRANSFER_FAILURE, req_str,
  843. self.zone_str(),
  844. format_addrinfo(self._master_addrinfo), str(e))
  845. ret = XFRIN_FAIL
  846. except Exception as e:
  847. # Catching all possible exceptions like this is generally not a
  848. # good practice, but handling an xfr session could result in
  849. # so many types of exceptions, including ones from the DNS library
  850. # or from the data source library. Eventually we'd introduce a
  851. # hierarchy for exception classes from a base "ISC exception" and
  852. # catch it here, but until then we need broadest coverage so that
  853. # we won't miss anything.
  854. logger.error(XFRIN_XFR_OTHER_FAILURE, req_str,
  855. self.zone_str(), str(e))
  856. ret = XFRIN_FAIL
  857. finally:
  858. # A xfrsuccess or xfrfail counter is incremented depending on
  859. # the result.
  860. result = {XFRIN_OK: 'xfrsuccess', XFRIN_FAIL: 'xfrfail'}[ret]
  861. self._counters.inc('zones', self._rrclass.to_text(),
  862. self._zone_name.to_text(), result)
  863. # The started statistics timer is finally stopped only in
  864. # a successful case.
  865. if ret == XFRIN_OK:
  866. self._counters.stop_timer('zones',
  867. self._rrclass.to_text(),
  868. self._zone_name.to_text(),
  869. 'last_' + req_str.lower() +
  870. '_duration')
  871. # Make sure any remaining transaction in the diff is closed
  872. # (if not yet - possible in case of xfr-level exception) as soon
  873. # as possible
  874. self._diff = None
  875. return ret
  876. def _check_response_header(self, msg):
  877. '''Perform minimal validation on responses'''
  878. # It's not clear how strict we should be about response validation.
  879. # BIND 9 ignores some cases where it would normally be considered a
  880. # bogus response. For example, it accepts a response even if its
  881. # opcode doesn't match that of the corresponding request.
  882. # According to an original developer of BIND 9 some of the missing
  883. # checks are deliberate to be kind to old implementations that would
  884. # cause interoperability trouble with stricter checks.
  885. msg_rcode = msg.get_rcode()
  886. if msg_rcode != Rcode.NOERROR:
  887. raise XfrinProtocolError('error response: %s' %
  888. msg_rcode.to_text())
  889. if not msg.get_header_flag(Message.HEADERFLAG_QR):
  890. raise XfrinProtocolError('response is not a response')
  891. if msg.get_qid() != self._query_id:
  892. raise XfrinProtocolError('bad query id')
  893. def _check_response_status(self, msg):
  894. '''Check validation of xfr response. '''
  895. self._check_response_header(msg)
  896. if msg.get_rr_count(Message.SECTION_QUESTION) > 1:
  897. raise XfrinProtocolError('query section count greater than 1')
  898. def _handle_xfrin_responses(self):
  899. read_next_msg = True
  900. while read_next_msg:
  901. data_len = self._get_request_response(2)
  902. msg_len = socket.htons(struct.unpack('H', data_len)[0])
  903. self._transfer_stats.byte_count += msg_len + 2
  904. recvdata = self._get_request_response(msg_len)
  905. msg = Message(Message.PARSE)
  906. msg.from_wire(recvdata, Message.PRESERVE_ORDER)
  907. self._transfer_stats.message_count += 1
  908. # TSIG related checks, including an unexpected signed response
  909. self._check_response_tsig(msg, recvdata)
  910. # Perform response status validation
  911. self._check_response_status(msg)
  912. for rr in msg.get_section(Message.SECTION_ANSWER):
  913. rr_handled = False
  914. while not rr_handled:
  915. rr_handled = self.__state.handle_rr(self, rr)
  916. read_next_msg = self.__state.finish_message(self)
  917. if self._shutdown_event.is_set():
  918. raise XfrinException('xfrin is forced to stop')
  919. def handle_read(self):
  920. '''Read query's response from socket. '''
  921. self._recvd_data = self.recv(self._need_recv_size)
  922. self._recvd_size = len(self._recvd_data)
  923. self._recv_time_out = False
  924. def writable(self):
  925. '''Ignore the writable socket. '''
  926. return False
  927. def __get_initial_xfr_type(zone_soa, request_ixfr, zname, zclass, master_addr):
  928. """Determine the initial xfr request type.
  929. This is a dedicated subroutine of __process_xfrin.
  930. """
  931. if zone_soa is None:
  932. # This is a kind of special case, so we log it at info level.
  933. logger.info(XFRIN_INITIAL_AXFR, format_zone_str(zname, zclass),
  934. AddressFormatter(master_addr))
  935. return RRType.AXFR
  936. if request_ixfr == ZoneInfo.REQUEST_IXFR_DISABLED:
  937. logger.debug(DBG_XFRIN_TRACE, XFRIN_INITIAL_IXFR_DISABLED,
  938. format_zone_str(zname, zclass),
  939. AddressFormatter(master_addr))
  940. return RRType.AXFR
  941. assert(request_ixfr == ZoneInfo.REQUEST_IXFR_FIRST or
  942. request_ixfr == ZoneInfo.REQUEST_IXFR_ONLY)
  943. logger.debug(DBG_XFRIN_TRACE, XFRIN_INITIAL_IXFR,
  944. format_zone_str(zname, zclass),
  945. AddressFormatter(master_addr))
  946. return RRType.IXFR
  947. def __process_xfrin(server, zone_name, rrclass, datasrc_client, zone_soa,
  948. shutdown_event, master_addrinfo, check_soa, tsig_key,
  949. request_ixfr, conn_class):
  950. conn = None
  951. exception = None
  952. ret = XFRIN_FAIL
  953. try:
  954. # Determine the initialreuqest type: AXFR or IXFR.
  955. request_type = __get_initial_xfr_type(zone_soa, request_ixfr,
  956. zone_name, rrclass,
  957. master_addrinfo[2])
  958. # Create a TCP connection for the XFR session and perform the
  959. # operation.
  960. sock_map = {}
  961. # In case we were asked to do IXFR and that one fails, we try again
  962. # with AXFR. But only if we could actually connect to the server.
  963. #
  964. # So we start with retry as True, which is set to false on each
  965. # attempt. In the case of connected but failed IXFR, we set it to true
  966. # once again.
  967. retry = True
  968. while retry:
  969. retry = False
  970. conn = conn_class(sock_map, zone_name, rrclass, datasrc_client,
  971. shutdown_event, master_addrinfo, zone_soa,
  972. tsig_key)
  973. conn.init_socket()
  974. ret = XFRIN_FAIL
  975. if conn.connect_to_master():
  976. ret = conn.do_xfrin(check_soa, request_type)
  977. if ret == XFRIN_FAIL and request_type == RRType.IXFR:
  978. # IXFR failed for some reason. It might mean the server
  979. # can't handle it, or we don't have the zone or we are out
  980. # of sync or whatever else. So we retry with with AXFR, as
  981. # it may succeed in many such cases; if "IXFR only" is
  982. # specified in request_ixfr, however, we suppress the
  983. # fallback.
  984. if request_ixfr == ZoneInfo.REQUEST_IXFR_ONLY:
  985. logger.warn(XFRIN_XFR_TRANSFER_FALLBACK_DISABLED,
  986. conn.zone_str())
  987. else:
  988. retry = True
  989. request_type = RRType.AXFR
  990. logger.warn(XFRIN_XFR_TRANSFER_FALLBACK,
  991. conn.zone_str())
  992. conn.close()
  993. conn = None
  994. except Exception as ex:
  995. # If exception happens, just remember it here so that we can re-raise
  996. # after cleaning up things. We don't log it here because we want
  997. # eliminate smallest possibility of having an exception in logging
  998. # itself.
  999. exception = ex
  1000. # asyncore.dispatcher requires explicit close() unless its lifetime
  1001. # from born to destruction is closed within asyncore.loop, which is not
  1002. # the case for us. We always close() here, whether or not do_xfrin
  1003. # succeeds, and even when we see an unexpected exception.
  1004. if conn is not None:
  1005. conn.close()
  1006. # Publish the zone transfer result news, so zonemgr can reset the
  1007. # zone timer, and xfrout can notify the zone's slaves if the result
  1008. # is success.
  1009. server.publish_xfrin_news(zone_name, rrclass, ret)
  1010. if exception is not None:
  1011. raise exception
  1012. def process_xfrin(server, xfrin_recorder, zone_name, rrclass, datasrc_client,
  1013. zone_soa, shutdown_event, master_addrinfo, check_soa,
  1014. tsig_key, request_ixfr, conn_class=XfrinConnection):
  1015. # Even if it should be rare, the main process of xfrin session can
  1016. # raise an exception. In order to make sure the lock in xfrin_recorder
  1017. # is released in any cases, we delegate the main part to the helper
  1018. # function in the try block, catch any exceptions, then release the lock.
  1019. xfrin_recorder.increment(zone_name)
  1020. exception = None
  1021. try:
  1022. __process_xfrin(server, zone_name, rrclass, datasrc_client, zone_soa,
  1023. shutdown_event, master_addrinfo, check_soa, tsig_key,
  1024. request_ixfr, conn_class)
  1025. except Exception as ex:
  1026. # don't log it until we complete decrement().
  1027. exception = ex
  1028. xfrin_recorder.decrement(zone_name)
  1029. if exception is not None:
  1030. if request_ixfr == ZoneInfo.REQUEST_IXFR_DISABLED:
  1031. typestr = "AXFR"
  1032. else:
  1033. typestr = "IXFR"
  1034. logger.error(XFRIN_XFR_PROCESS_FAILURE, typestr, zone_name.to_text(),
  1035. str(rrclass), str(exception))
  1036. class XfrinRecorder:
  1037. def __init__(self):
  1038. self._lock = threading.Lock()
  1039. self._zones = []
  1040. def increment(self, zone_name):
  1041. self._lock.acquire()
  1042. self._zones.append(zone_name)
  1043. self._lock.release()
  1044. def decrement(self, zone_name):
  1045. self._lock.acquire()
  1046. if zone_name in self._zones:
  1047. self._zones.remove(zone_name)
  1048. self._lock.release()
  1049. def xfrin_in_progress(self, zone_name):
  1050. self._lock.acquire()
  1051. ret = zone_name in self._zones
  1052. self._lock.release()
  1053. return ret
  1054. def count(self):
  1055. self._lock.acquire()
  1056. ret = len(self._zones)
  1057. self._lock.release()
  1058. return ret
  1059. class ZoneInfo:
  1060. # Internal values corresponding to request_ixfr
  1061. REQUEST_IXFR_FIRST = 0 # request_ixfr=yes, use IXFR 1st then AXFR
  1062. REQUEST_IXFR_ONLY = 1 # request_ixfr=only, use IXFR only
  1063. REQUEST_IXFR_DISABLED = 2 # request_ixfr=no, AXFR-only
  1064. # Map from configuration values for request_ixfr to internal values
  1065. # This is a constant; don't modify.
  1066. REQUEST_IXFR_CFG_TO_VAL = { 'yes': REQUEST_IXFR_FIRST,
  1067. 'only': REQUEST_IXFR_ONLY,
  1068. 'no': REQUEST_IXFR_DISABLED }
  1069. def __init__(self, config_data, module_cc):
  1070. """Creates a zone_info with the config data element as
  1071. specified by the 'zones' list in xfrin.spec. Module_cc is
  1072. needed to get the defaults from the specification"""
  1073. # Handle deprecated config parameter explicitly for the moment.
  1074. if config_data.get('use_ixfr') is not None:
  1075. raise XfrinZoneInfoException('use_ixfr was deprecated.' +
  1076. 'use rquest_ixfr')
  1077. self._module_cc = module_cc
  1078. self.set_name(config_data.get('name'))
  1079. self.set_master_addr(config_data.get('master_addr'))
  1080. self.set_master_port(config_data.get('master_port'))
  1081. self.set_zone_class(config_data.get('class'))
  1082. self.set_tsig_key_name(config_data.get('tsig_key'))
  1083. self.set_request_ixfr(config_data.get('request_ixfr'))
  1084. @property
  1085. def request_ixfr(self):
  1086. """Policy on the use of IXFR.
  1087. Possible values are REQUEST_IXFR_xxx, internally stored in
  1088. __request_ixfr, read-only outside of the class.
  1089. """
  1090. return self.__request_ixfr
  1091. def set_name(self, name_str):
  1092. """Set the name for this zone given a name string.
  1093. Raises XfrinZoneInfoException if name_str is None or if it
  1094. cannot be parsed."""
  1095. if name_str is None:
  1096. raise XfrinZoneInfoException("Configuration zones list "
  1097. "element does not contain "
  1098. "'name' attribute")
  1099. else:
  1100. self.name = _check_zone_name(name_str)
  1101. def set_master_addr(self, master_addr_str):
  1102. """Set the master address for this zone given an IP address
  1103. string. Raises XfrinZoneInfoException if master_addr_str is
  1104. None or if it cannot be parsed."""
  1105. if master_addr_str is None:
  1106. raise XfrinZoneInfoException("master address missing from config data")
  1107. else:
  1108. try:
  1109. self.master_addr = isc.net.parse.addr_parse(master_addr_str)
  1110. except ValueError:
  1111. logger.error(XFRIN_BAD_MASTER_ADDR_FORMAT, master_addr_str)
  1112. errmsg = "bad format for zone's master: " + master_addr_str
  1113. raise XfrinZoneInfoException(errmsg)
  1114. def set_master_port(self, master_port_str):
  1115. """Set the master port given a port number string. If
  1116. master_port_str is None, the default from the specification
  1117. for this module will be used. Raises XfrinZoneInfoException if
  1118. the string contains an invalid port number"""
  1119. if master_port_str is None:
  1120. self.master_port = self._module_cc.get_default_value("zones/master_port")
  1121. else:
  1122. try:
  1123. self.master_port = isc.net.parse.port_parse(master_port_str)
  1124. except ValueError:
  1125. logger.error(XFRIN_BAD_MASTER_PORT_FORMAT, master_port_str)
  1126. errmsg = "bad format for zone's master port: " + master_port_str
  1127. raise XfrinZoneInfoException(errmsg)
  1128. def set_zone_class(self, zone_class_str):
  1129. """Set the zone class given an RR class str (e.g. "IN"). If
  1130. zone_class_str is None, it will default to what is specified
  1131. in the specification file for this module. Raises
  1132. XfrinZoneInfoException if the string cannot be parsed."""
  1133. # TODO: remove _str
  1134. self.class_str = zone_class_str or self._module_cc.get_default_value("zones/class")
  1135. if zone_class_str == None:
  1136. #TODO rrclass->zone_class
  1137. self.rrclass = RRClass(self._module_cc.get_default_value("zones/class"))
  1138. else:
  1139. try:
  1140. self.rrclass = RRClass(zone_class_str)
  1141. except InvalidRRClass:
  1142. logger.error(XFRIN_BAD_ZONE_CLASS, zone_class_str)
  1143. errmsg = "invalid zone class: " + zone_class_str
  1144. raise XfrinZoneInfoException(errmsg)
  1145. def set_tsig_key_name(self, tsig_key_str):
  1146. """Set the name of the tsig_key for this zone. If tsig_key_str
  1147. is None, no TSIG key will be used. This name is used to
  1148. find the TSIG key to use for transfers in the global TSIG
  1149. key ring.
  1150. Raises XfrinZoneInfoException if tsig_key_str is not a valid
  1151. (dns) name."""
  1152. if tsig_key_str is None:
  1153. self.tsig_key_name = None
  1154. else:
  1155. # can throw a number of exceptions but it is just one
  1156. # call, so Exception should be OK here
  1157. try:
  1158. self.tsig_key_name = Name(tsig_key_str)
  1159. except Exception as exc:
  1160. raise XfrinZoneInfoException("Bad TSIG key name: " + str(exc))
  1161. def get_tsig_key(self):
  1162. if self.tsig_key_name is None:
  1163. return None
  1164. result, key = get_keyring().find(self.tsig_key_name)
  1165. if result != isc.dns.TSIGKeyRing.SUCCESS:
  1166. raise XfrinZoneInfoException("TSIG key not found in keyring: " +
  1167. self.tsig_key_name.to_text())
  1168. else:
  1169. return key
  1170. def set_request_ixfr(self, request_ixfr):
  1171. if request_ixfr is None:
  1172. request_ixfr = \
  1173. self._module_cc.get_default_value("zones/request_ixfr")
  1174. try:
  1175. self.__request_ixfr = self.REQUEST_IXFR_CFG_TO_VAL[request_ixfr]
  1176. except KeyError:
  1177. raise XfrinZoneInfoException('invalid value for request_ixfr: ' +
  1178. request_ixfr)
  1179. def get_master_addr_info(self):
  1180. return (self.master_addr.family, socket.SOCK_STREAM,
  1181. (str(self.master_addr), self.master_port))
  1182. def _do_auth_loadzone(server, zone_name, zone_class):
  1183. msg = auth_loadzone_command(server._module_cc, zone_name, zone_class)
  1184. if msg is not None:
  1185. param = msg['command'][1]
  1186. logger.debug(DBG_XFRIN_TRACE, XFRIN_AUTH_LOADZONE, param["origin"],
  1187. param["class"])
  1188. seq = server._send_cc_session.group_sendmsg(msg, AUTH_MODULE_NAME,
  1189. want_answer=True)
  1190. answer, env = server._send_cc_session.group_recvmsg(False, seq)
  1191. class Xfrin:
  1192. def __init__(self):
  1193. self._max_transfers_in = 10
  1194. self._zones = {}
  1195. self.recorder = XfrinRecorder()
  1196. self._shutdown_event = threading.Event()
  1197. self._counters = Counters(SPECFILE_LOCATION)
  1198. # This is essentially private, but we allow tests to customize it.
  1199. self._datasrc_clients_mgr = DataSrcClientsMgr()
  1200. # Initial configuration
  1201. self._cc_setup()
  1202. config_data = self._module_cc.get_full_config()
  1203. self.config_handler(config_data)
  1204. # data_sources configuration should be ready with cfgmgr, so this
  1205. # shouldn't fail; if it ever does we simply propagate the exception
  1206. # to terminate the program.
  1207. self._module_cc.add_remote_config_by_name('data_sources',
  1208. self._datasrc_config_handler)
  1209. init_keyring(self._module_cc)
  1210. def _cc_setup(self):
  1211. '''This method is used only as part of initialization, but is
  1212. implemented separately for convenience of unit tests; by letting
  1213. the test code override this method we can test most of this class
  1214. without requiring a command channel.'''
  1215. # Create one session for sending command to other modules, because the
  1216. # listening session will block the send operation.
  1217. self._send_cc_session = isc.cc.Session()
  1218. self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
  1219. self.config_handler,
  1220. self.command_handler)
  1221. self._module_cc.start()
  1222. def _cc_check_command(self):
  1223. '''This is a straightforward wrapper for cc.check_command,
  1224. but provided as a separate method for the convenience
  1225. of unit tests.'''
  1226. self._module_cc.check_command(False)
  1227. def _get_zone_info(self, name, rrclass):
  1228. """Returns the ZoneInfo object containing the configured data
  1229. for the given zone name. If the zone name did not have any
  1230. data, returns None"""
  1231. return self._zones.get((name.to_text(), rrclass.to_text()))
  1232. def _add_zone_info(self, zone_info):
  1233. """Add the zone info. Raises a XfrinZoneInfoException if a zone
  1234. with the same name and class is already configured"""
  1235. key = (zone_info.name.to_text(), zone_info.class_str)
  1236. if key in self._zones:
  1237. raise XfrinZoneInfoException("zone " + str(key) +
  1238. " configured multiple times")
  1239. self._zones[key] = zone_info
  1240. def _clear_zone_info(self):
  1241. self._zones = {}
  1242. def config_handler(self, new_config):
  1243. # backup all config data (should there be a problem in the new
  1244. # data)
  1245. old_max_transfers_in = self._max_transfers_in
  1246. old_zones = self._zones
  1247. self._max_transfers_in = \
  1248. new_config.get("transfers_in") or self._max_transfers_in
  1249. if 'zones' in new_config:
  1250. self._clear_zone_info()
  1251. for zone_config in new_config.get('zones'):
  1252. try:
  1253. zone_info = ZoneInfo(zone_config, self._module_cc)
  1254. self._add_zone_info(zone_info)
  1255. except XfrinZoneInfoException as xce:
  1256. self._zones = old_zones
  1257. self._max_transfers_in = old_max_transfers_in
  1258. return create_answer(1, str(xce))
  1259. return create_answer(0)
  1260. def _datasrc_config_handler(self, new_config, config_data):
  1261. """Configuration handler of the 'data_sources' module.
  1262. The actual handling is deletegated to the DataSrcClientsMgr class;
  1263. this method is a simple wrapper.
  1264. This is essentially private, but implemented as 'protected' so tests
  1265. can refer to it; other external use is prohibited.
  1266. """
  1267. try:
  1268. self._datasrc_clients_mgr.reconfigure(new_config)
  1269. except isc.server_common.datasrc_clients_mgr.ConfigError as ex:
  1270. logger.error(XFRIN_DATASRC_CONFIG_ERROR, ex)
  1271. def shutdown(self):
  1272. ''' shutdown the xfrin process. the thread which is doing xfrin should be
  1273. terminated.
  1274. '''
  1275. self._module_cc.send_stopping()
  1276. self._shutdown_event.set()
  1277. main_thread = threading.currentThread()
  1278. for th in threading.enumerate():
  1279. if th is main_thread:
  1280. continue
  1281. th.join()
  1282. def __validate_notify_addr(self, notify_addr, zone_str, zone_info):
  1283. """Validate notify source as a destination for xfr source.
  1284. This is called from __handle_xfr_command in case xfr is triggered
  1285. by ZoneMgr either due to incoming Notify or periodic refresh event.
  1286. """
  1287. if zone_info is None:
  1288. # TODO what to do? no info known about zone. defaults?
  1289. errmsg = "Got notification to retransfer unknown zone " + zone_str
  1290. logger.info(XFRIN_RETRANSFER_UNKNOWN_ZONE, zone_str)
  1291. return create_answer(1, errmsg)
  1292. else:
  1293. master_addr = zone_info.get_master_addr_info()
  1294. if (notify_addr[0] != master_addr[0] or
  1295. notify_addr[2] != master_addr[2]):
  1296. notify_addr_str = format_addrinfo(notify_addr)
  1297. master_addr_str = format_addrinfo(master_addr)
  1298. errmsg = "Got notification for " + zone_str\
  1299. + "from unknown address: " + notify_addr_str;
  1300. logger.info(XFRIN_NOTIFY_UNKNOWN_MASTER, zone_str,
  1301. notify_addr_str, master_addr_str)
  1302. return create_answer(1, errmsg)
  1303. # Notified address is okay
  1304. return None
  1305. def __get_running_request_ixfr(self, arg_request_ixfr, zone_info):
  1306. """Determine the request_ixfr policy for a specific transfer.
  1307. This is a dedicated subroutine of __handle_xfr_command.
  1308. """
  1309. # If explicitly specified, use it.
  1310. if arg_request_ixfr is not None:
  1311. return arg_request_ixfr
  1312. # Otherwise, if zone info is known, use its value.
  1313. if zone_info is not None:
  1314. return zone_info.request_ixfr
  1315. # Otherwise, use the default value for ZoneInfo
  1316. request_ixfr_def = \
  1317. self._module_cc.get_default_value("zones/request_ixfr")
  1318. return ZoneInfo.REQUEST_IXFR_CFG_TO_VAL[request_ixfr_def]
  1319. def __handle_xfr_command(self, args, check_soa, addr_validator,
  1320. request_ixfr):
  1321. """Common subroutine for handling transfer commands.
  1322. This helper method unifies both cases of transfer command from
  1323. ZoneMgr or from a user. Depending on who invokes the transfer,
  1324. details of validation and parameter selection slightly vary.
  1325. These conditions are passed through parameters and handled in the
  1326. unified code of this method accordingly.
  1327. If this is from the ZoneMgr due to incoming notify, zone transfer
  1328. should start from the notify's source address as long as it's
  1329. configured as a master address, according to RFC1996. The current
  1330. implementation conforms to it in a limited way: we can only set one
  1331. master address. Once we add the ability to have multiple master
  1332. addresses, we should check if it matches one of them, and then use it.
  1333. In case of transfer command from the user, if the command specifies
  1334. the master address, use that one; otherwise try to use a configured
  1335. master address for the zone.
  1336. """
  1337. (zone_name, rrclass) = self._parse_zone_name_and_class(args)
  1338. master_addr = self._parse_master_and_port(args, zone_name, rrclass)
  1339. zone_info = self._get_zone_info(zone_name, rrclass)
  1340. tsig_key = None if zone_info is None else zone_info.get_tsig_key()
  1341. zone_str = format_zone_str(zone_name, rrclass) # for logging
  1342. answer = addr_validator(master_addr, zone_str, zone_info)
  1343. if answer is not None:
  1344. return answer
  1345. request_ixfr = self.__get_running_request_ixfr(request_ixfr, zone_info)
  1346. ret = self.xfrin_start(zone_name, rrclass, master_addr, tsig_key,
  1347. request_ixfr, check_soa)
  1348. return create_answer(ret[0], ret[1])
  1349. def command_handler(self, command, args):
  1350. logger.debug(DBG_XFRIN_TRACE, XFRIN_RECEIVED_COMMAND, command)
  1351. answer = create_answer(0)
  1352. try:
  1353. if command == 'shutdown':
  1354. self._shutdown_event.set()
  1355. elif command == 'notify' or command == REFRESH_FROM_ZONEMGR:
  1356. # refresh/notify command from zone manager.
  1357. # The address has to be validated and always perform SOA check.
  1358. addr_validator = \
  1359. lambda x, y, z: self.__validate_notify_addr(x, y, z)
  1360. answer = self.__handle_xfr_command(args, True, addr_validator,
  1361. None)
  1362. elif command == 'retransfer':
  1363. # retransfer from cmdctl (sent by bindctl).
  1364. # No need for address validation, skip SOA check, and always
  1365. # use AXFR.
  1366. answer = self.__handle_xfr_command(
  1367. args, False, lambda x, y, z: None,
  1368. ZoneInfo.REQUEST_IXFR_DISABLED)
  1369. elif command == 'refresh':
  1370. # retransfer from cmdctl (sent by bindctl). similar to
  1371. # retransfer, but do SOA check, and honor request_ixfr config.
  1372. answer = self.__handle_xfr_command(
  1373. args, True, lambda x, y, z: None, None)
  1374. # return statistics data to the stats daemon
  1375. elif command == "getstats":
  1376. # The log level is here set to debug in order to avoid
  1377. # that a log becomes too verbose. Because the
  1378. # b10-stats daemon is periodically asking to the
  1379. # b10-xfrin daemon.
  1380. answer = create_answer(0, self._counters.get_statistics())
  1381. else:
  1382. answer = create_answer(1, 'unknown command: ' + command)
  1383. except XfrinException as err:
  1384. logger.error(XFRIN_COMMAND_ERROR, command, str(err))
  1385. answer = create_answer(1, str(err))
  1386. return answer
  1387. def _parse_zone_name_and_class(self, args):
  1388. zone_name_str = args.get('zone_name')
  1389. if zone_name_str is None:
  1390. raise XfrinException('zone name should be provided')
  1391. return (_check_zone_name(zone_name_str),
  1392. _check_zone_class(args.get('zone_class')))
  1393. def _parse_master_and_port(self, args, zone_name, zone_class):
  1394. """
  1395. Return tuple (family, socktype, sockaddr) for address and port in given
  1396. args dict.
  1397. IPv4 and IPv6 are the only supported addresses now, so sockaddr will be
  1398. (address, port). The socktype is socket.SOCK_STREAM for now.
  1399. """
  1400. # check if we have configured info about this zone, in case
  1401. # port or master are not specified
  1402. zone_info = self._get_zone_info(zone_name, zone_class)
  1403. addr_str = args.get('master')
  1404. if addr_str is None:
  1405. if zone_info is not None:
  1406. addr = zone_info.master_addr
  1407. else:
  1408. raise XfrinException("Master address not given or "
  1409. "configured for " + zone_name.to_text())
  1410. else:
  1411. try:
  1412. addr = isc.net.parse.addr_parse(addr_str)
  1413. except ValueError as err:
  1414. raise XfrinException("failed to resolve master address %s: %s" %
  1415. (addr_str, str(err)))
  1416. port_str = args.get('port')
  1417. if port_str is None:
  1418. if zone_info is not None:
  1419. port = zone_info.master_port
  1420. else:
  1421. port = DEFAULT_MASTER_PORT
  1422. else:
  1423. try:
  1424. port = isc.net.parse.port_parse(port_str)
  1425. except ValueError as err:
  1426. raise XfrinException("failed to parse port=%s: %s" %
  1427. (port_str, str(err)))
  1428. return (addr.family, socket.SOCK_STREAM, (str(addr), port))
  1429. def publish_xfrin_news(self, zone_name, zone_class, xfr_result):
  1430. '''Send command to xfrout/zone manager module.
  1431. If xfrin has finished successfully for one zone, tell the good
  1432. news(command: zone_new_data_ready) to zone manager and xfrout.
  1433. if xfrin failed, just tell the bad news to zone manager, so that
  1434. it can reset the refresh timer for that zone. '''
  1435. param = {'zone_name': zone_name.to_text(),
  1436. 'zone_class': zone_class.to_text()}
  1437. if xfr_result == XFRIN_OK:
  1438. # FIXME: Due to the hack with two different CC sessions
  1439. # (see the _cc_setup comment) and the fact the rpc_call
  1440. # is a high-level call present only at ModuleCCSession,
  1441. # we are forced to use the primitive way of manually
  1442. # calling group_sendmsg and the group_recvmsg. Also, why
  1443. # do we do group_recvmsg when we don't need the answer?
  1444. # And why is this direct RPC call if a notification would
  1445. # be more appropriate?
  1446. _do_auth_loadzone(self, zone_name, zone_class)
  1447. msg = create_command(notify_out.ZONE_NEW_DATA_READY_CMD, param)
  1448. # catch the exception, in case msgq has been killed.
  1449. try:
  1450. seq = self._send_cc_session.group_sendmsg(msg,
  1451. XFROUT_MODULE_NAME,
  1452. want_answer=True)
  1453. try:
  1454. answer, env = self._send_cc_session.group_recvmsg(False,
  1455. seq)
  1456. except isc.cc.session.SessionTimeout:
  1457. pass # for now we just ignore the failure
  1458. seq = self._send_cc_session.group_sendmsg(msg,
  1459. ZONE_MANAGER_MODULE_NAME,
  1460. want_answer=True)
  1461. try:
  1462. answer, env = self._send_cc_session.group_recvmsg(False,
  1463. seq)
  1464. except isc.cc.session.SessionTimeout:
  1465. pass # for now we just ignore the failure
  1466. except socket.error as err:
  1467. logger.error(XFRIN_MSGQ_SEND_ERROR, XFROUT_MODULE_NAME, ZONE_MANAGER_MODULE_NAME)
  1468. else:
  1469. msg = create_command(notify_out.ZONE_XFRIN_FAILED, param)
  1470. # catch the exception, in case msgq has been killed.
  1471. try:
  1472. seq = self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME,
  1473. want_answer=True)
  1474. try:
  1475. answer, env = self._send_cc_session.group_recvmsg(False,
  1476. seq)
  1477. except isc.cc.session.SessionTimeout:
  1478. pass # for now we just ignore the failure
  1479. except socket.error as err:
  1480. logger.error(XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER, ZONE_MANAGER_MODULE_NAME)
  1481. def startup(self):
  1482. logger.debug(DBG_PROCESS, XFRIN_STARTED)
  1483. while not self._shutdown_event.is_set():
  1484. self._cc_check_command()
  1485. def xfrin_start(self, zone_name, rrclass, master_addrinfo, tsig_key,
  1486. request_ixfr, check_soa=True):
  1487. # check max_transfer_in, else return quota error
  1488. if self.recorder.count() >= self._max_transfers_in:
  1489. return (1, 'xfrin quota error')
  1490. if self.recorder.xfrin_in_progress(zone_name):
  1491. return (1, 'zone xfrin is in progress')
  1492. # Identify the data source to which the zone content is transferred,
  1493. # and get the current zone SOA from the data source (if available).
  1494. # Note that we do this before spawning the xfrin session thread.
  1495. # find() on the client list and use of ZoneFinder (in _get_zone_soa())
  1496. # should be completed within the same single thread.
  1497. datasrc_client = None
  1498. clist = self._datasrc_clients_mgr.get_client_list(rrclass)
  1499. if clist is None:
  1500. return (1, 'no data source is configured for class %s' % rrclass)
  1501. try:
  1502. datasrc_client = clist.find(zone_name, True, False)[0]
  1503. if datasrc_client is None: # can happen, so log it separately.
  1504. logger.error(XFRIN_DATASRC_UNKNOWN,
  1505. format_zone_str(zone_name, rrclass))
  1506. return (1, 'data source to transfer %s to is unknown' %
  1507. format_zone_str(zone_name, rrclass))
  1508. zone_soa = _get_zone_soa(datasrc_client, zone_name, rrclass)
  1509. except isc.datasrc.Error as ex:
  1510. # rare case error. re-raise as XfrinException so it'll be logged
  1511. # in command_handler().
  1512. raise XfrinException('unexpected failure in datasrc module: ' +
  1513. str(ex))
  1514. xfrin_thread = threading.Thread(target=process_xfrin,
  1515. args=(self, self.recorder,
  1516. zone_name, rrclass,
  1517. datasrc_client, zone_soa,
  1518. self._shutdown_event,
  1519. master_addrinfo, check_soa,
  1520. tsig_key, request_ixfr))
  1521. xfrin_thread.start()
  1522. return (0, 'zone xfrin is started')
  1523. def _get_zone_soa(datasrc_client, zone_name, zone_class):
  1524. """Retrieve the current SOA RR of the zone to be transferred.
  1525. This function is essentially private to the module, but will also
  1526. be called (or tweaked) from tests; no one else should use this
  1527. function directly.
  1528. The specified zone is expected to exist in the data source referenced
  1529. by the given datasrc_client at the point of the call to this function.
  1530. If this is not met XfrinException exception will be raised.
  1531. It will be used for various purposes in subsequent xfr protocol
  1532. processing. It is validly possible that the zone is currently
  1533. empty and therefore doesn't have an SOA, so this method doesn't
  1534. consider it an error and returns None in such a case. It may or
  1535. may not result in failure in the actual processing depending on
  1536. how the SOA is used.
  1537. When the zone has an SOA RR, this method makes sure that it's
  1538. valid, i.e., it has exactly one RDATA; if it is not the case
  1539. this method returns None.
  1540. """
  1541. # get the zone finder. this must be SUCCESS (not even
  1542. # PARTIALMATCH) because we are specifying the zone origin name.
  1543. result, finder = datasrc_client.find_zone(zone_name)
  1544. if result != DataSourceClient.SUCCESS:
  1545. # The data source doesn't know the zone. In the context of this
  1546. # function is called, this shouldn't happen.
  1547. raise XfrinException("unexpected result: zone %s doesn't exist" %
  1548. format_zone_str(zone_name, zone_class))
  1549. result, soa_rrset, _ = finder.find(zone_name, RRType.SOA)
  1550. if result != ZoneFinder.SUCCESS:
  1551. logger.info(XFRIN_ZONE_NO_SOA, format_zone_str(zone_name, zone_class))
  1552. return None
  1553. if soa_rrset.get_rdata_count() != 1:
  1554. logger.warn(XFRIN_ZONE_MULTIPLE_SOA,
  1555. format_zone_str(zone_name, zone_class),
  1556. soa_rrset.get_rdata_count())
  1557. return None
  1558. return soa_rrset
  1559. xfrind = None
  1560. def signal_handler(signal, frame):
  1561. if xfrind:
  1562. xfrind.shutdown()
  1563. sys.exit(0)
  1564. def set_signal_handler():
  1565. signal.signal(signal.SIGTERM, signal_handler)
  1566. signal.signal(signal.SIGINT, signal_handler)
  1567. def set_cmd_options(parser):
  1568. parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
  1569. help="This option is obsolete and has no effect.")
  1570. def main(xfrin_class, use_signal=True):
  1571. """The main loop of the Xfrin daemon.
  1572. @param xfrin_class: A class of the Xfrin object. This is normally Xfrin,
  1573. but can be a subclass of it for customization.
  1574. @param use_signal: True if this process should catch signals. This is
  1575. normally True, but may be disabled when this function is called in a
  1576. testing context."""
  1577. global xfrind
  1578. try:
  1579. parser = OptionParser(version = __version__)
  1580. set_cmd_options(parser)
  1581. (options, args) = parser.parse_args()
  1582. if use_signal:
  1583. set_signal_handler()
  1584. xfrind = xfrin_class()
  1585. xfrind.startup()
  1586. except KeyboardInterrupt:
  1587. logger.info(XFRIN_STOPPED_BY_KEYBOARD)
  1588. except isc.cc.session.SessionError as e:
  1589. logger.error(XFRIN_CC_SESSION_ERROR, str(e))
  1590. except Exception as e:
  1591. logger.error(XFRIN_UNKNOWN_ERROR, str(e))
  1592. if xfrind:
  1593. xfrind.shutdown()
  1594. logger.info(XFRIN_EXITING)
  1595. if __name__ == '__main__':
  1596. main(Xfrin)