Closed schanzen closed 1 month ago
This is definitely a bug
AXFR-style IXFR messages aren't a problem; the server is allowed to return AXFR-style responses to an IXFR query, and dnspython is correctly detecting this, and parsing the response as an AXFR. The problem is that this is also causing the expected type in future question sections to be AXFR, which is wrong, as the query type was IXFR.
There aren't any tests which cover AXFR-style IXFR responses that span multiple messages, and probably should be.
I am not entirely convinced that the bug is that dnspython incorrectly handles AXFR responses to IXFR queries (but it very well may be doing that incorrectly as well). After all, the reponse is IXFR, which is why it fails: dnspython heuristically expects AXFR but the server claims it is sending an IXFR response.
When executing the dig command in the OP the first RR you get is the SOA. Tracing what is happening in dnspython it seems as is the first SOA is either lost or the response is different from what dig outputs. I have not looked at wireshark yet which would probably give a better picture wrt dig vs dnspython. In any case, I would expect my rather simple snippet to just work, and not error.
I think your analysis is incorrect. For one thing, there are no heuristics; dnspython is following the IXFR protocol. See the first paragraph of https://datatracker.ietf.org/doc/html/rfc1995#section-4.
If incremental zone transfer is not available, the entire zone is
returned. The first and the last RR of the response is the SOA
record of the zone. I.e. the behavior is the same as an AXFR
response except the query type is IXFR.
This is the format of the response here, and dnspython is detecting this by noticing that the response begins with a single SOA. As I said, the problem is that dnspython is incorrectly checking subsequent messages have type AXFR; it should be checking that they have type IXFR, to match the query.
I'm not sure why you think SOA records are being lost. If you use dns.query.xfr
instead of dns.query.inbound_xfr
and print the messages, you'd see the first one prints correctly, and an exception will be thrown when the second one is processed. If dnspython is modified to remove the incorrect type check, the transfer will complete successfully.
In any case, I would expect my rather simple snippet to just work, and not error.
Yes, that's why I started with "This is definitely a bug."
Sorry I mistakenly commented on the pull request instead of here. Anyway, your fix works for me for both issues, thanks!
What I still wonder is how I can as a caller determine if an IXFR or AXFR is performed on my transaction manager/zone.
Ok I actually do sometimes(!) get the error I posted on the commit with serial=None
after all:
Traceback (most recent call last):
File "/home/schanzen/dev/ascension/test_error.py", line 7, in <module>
dns.query.inbound_xfr("195.43.86.175", zone, txn)
File "/home/schanzen/dev/ascension/.venv/lib64/python3.12/site-packages/dns/query.py", line 1665, in inbound_xfr
for _ in _inbound_xfr(txn_manager, s, query, serial, timeout, expiration):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/schanzen/dev/ascension/.venv/lib64/python3.12/site-packages/dns/query.py", line 1470, in _inbound_xfr
done = inbound.process_message(r)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/schanzen/dev/ascension/.venv/lib64/python3.12/site-packages/dns/xfr.py", line 218, in process_message
raise dns.exception.FormError("unexpected origin SOA in AXFR")
dns.exception.FormError: unexpected origin SOA in AXFR
So something the server returns when AXFR is requested makes dnspython unhappy still. But only sometimes?
Sorry, just meant to merge Brian's changes not close the issue altogether.
Regardless of being initiated as an AXFR or an IXFR that falls back to AXFR, you can tell if you are getting a full zone transfer and not an incremental by looking at the transaction's replacement
attribute, which will be True
.
I will look into "unexpected origin SOA in AXFR"
fwiw I noticed that the serial of the first and last SOA sometimes differ, probably becuase it changed internally. .ee
seems to update the serial very frequently...
I an not sure if it explicitly violates the spec
An AXFR response that is transferring the zone's contents will
consist of a series (which could be a series of length 1) of DNS
messages. In such a series, the first message MUST begin with the
SOA resource record of the zone, and the last message MUST conclude
with the same SOA resource record.
of if that can still be considered the same SOA.
Yes, I just replicated the issue with serial=None, and this is a problem with the authority. A zone transfer is supposed to be a single coherent version, not a mix of versions (RFC 1035 section 6.3). In the test I did, dnspython got a serial of 1729270216 at the start of the AXFR, and 1729270218 at the end, which is what triggers the error, as dnspython expects the AXFR to end with the same SOA with which it started.
And in the text you quoted from RFC 5936, a change in the serial number is definitely not "the same SOA", though the standard could be clearer!
Now closing as fixed.
Describe the bug I am trying to do an IXFR for
ee.
fromzone.internet.ee.
. This works fine with dig, for example usingdig ixfr=1720121412 ee @zone.internet.ee.
However, with dnspython, for some reason the first (and crucial) SOA record is missing and then it falls back to AXFR and then fails on some assertion/sanity check:(it sees that the question has type IXFR and expects AXFR now)
To Reproduce
Use this script:
The IP is the result of
host zone.internet.ee
. The serial is picked from an old revision. The behaviour may change at some point if the serial is too old (then the server will probably actually fall back to AXFR and the above code will run fine).Context (please complete the following information):