Strange response to mangled requests

Testing robustness of servers, I see that Unbound reacts in a way I
don't approve when the incoming request is malformed. Instead of
replying with FORMERR, it echoes the invalid request.

The attached Python script shows this behavior. With Unbound 1.10.0, I
get:

% ./test_pub_resolv-trunc_data.py 127.0.0.1 9053
data sent: b'\xee\xd0\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x04cu'
      hex: ee d0 01 00 00 01 00 00 00 00 00 00 04 63 75
      bin: 11101110 11010000 00000001 00000000 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000100 01100011 01110101

data recv: b'\xee\xd0\x81\x01\x00\x01\x00\x00\x00\x00\x00\x00\x04cu'
      hex: ee d0 81 01 00 01 00 00 00 00 00 00 04 63 75
      bin: 11101110 11010000 10000001 00000001 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000100 01100011 01110101

Traceback (most recent call last):
  File "./test_pub_resolv-trunc_data.py", line 51, in <module>
    resp = dns.message.from_wire(r_data)
  File "/usr/lib/python3/dist-packages/dns/message.py", line 823, in from_wire
    reader.read()
  File "/usr/lib/python3/dist-packages/dns/message.py", line 746, in read
    self._get_question(qcount)
  File "/usr/lib/python3/dist-packages/dns/message.py", line 621, in _get_question
    (qname, used) = dns.name.from_wire(self.wire, self.current)
  File "/usr/lib/python3/dist-packages/dns/name.py", line 975, in from_wire
    labels.append(message[current: current + count].unwrap())
  File "/usr/lib/python3/dist-packages/dns/wiredata.py", line 71, in __getitem__
    raise dns.exception.FormError
dns.exception.FormError: DNS message is malformed.

While I was expecting (this is with BIND):

% ./test_pub_resolv-trunc_data.py 127.0.0.1
data sent: b'\xfc\xca\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x04cu'
      hex: fc ca 01 00 00 01 00 00 00 00 00 00 04 63 75
      bin: 11111100 11001010 00000001 00000000 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000100 01100011 01110101

data recv: b'\xfc\xca\x81\x01\x00\x00\x00\x00\x00\x00\x00\x00'
      hex: fc ca 81 01 00 00 00 00 00 00 00 00
      bin: 11111100 11001010 10000001 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

id 64714
opcode QUERY
rcode FORMERR
flags QR RD
;QUESTION
;ANSWER
;AUTHORITY
;ADDITIONAL

a message of 50 lines which said:

The attached Python script

Really attached, this time.

(attachments)

test_pub_resolv-trunc_data.py (1.11 KB)

Hi Stephane,

Unbound echoes the request. But it really sets RCODE=FORMERR on the
reply. Also it sets the QR flag on the reply (as usual). And this is
visible in the hex dump you have. (the 4th byte moves from '00'
(NOERROR) to '01' (FORMERR)).

What BIND did was remove the 'bad' question section that it could not
parse in the reply. Unbound could do that too, but today, it copies it
to the reply. It is a matter of preference I think, because if you sent
it I can reply with that, I think that could be clearer, although you
can also make a case that removal of the bad material is somehow good,
probably depends on the 'use case' of formerr inputs, but I do not know
of any.

Best regards, Wouter

a message of 69 lines which said:

probably depends on the 'use case' of formerr inputs, but I do not
know of any.

Here, the use case was to test DoT servers (the script I sent uses UDP
but DoT shows the same issue) for compliance. The library I use parses
the entire DNS message (and fails) before I can read the rcode so
Unbound's current reply does not help me.