ruby / resolv

A thread-aware DNS resolver library written in Ruby
Other
35 stars 28 forks source link

crash when query long TXT RRs #13

Open Tietew opened 2 years ago

Tietew commented 2 years ago

Reproducible code

irb(main):001:0> require 'resolv'
irb(main):002:0> Resolv::DNS.new.getresources('google.com', Resolv::DNS::Resource::IN::TXT)

Expected result:

Returns TXT records (see below)

Actual result:

.../resolv.rb:898:in `recv_reply': undefined method `unpack' for nil:NilClass (NoMethodError)
        from .../resolv.rb:687:in `request'
        from .../resolv.rb:531:in `block in fetch_resource'
        from .../lib/resolv.rb:1125:in `block (3 levels) in resolv'
        from .../lib/resolv.rb:1123:in `each'
        from .../lib/resolv.rb:1123:in `block (2 levels) in resolv'
        from .../lib/resolv.rb:1122:in `each'
        from .../lib/resolv.rb:1122:in `block in resolv'
        from .../lib/resolv.rb:1120:in `each'
        from .../lib/resolv.rb:1120:in `resolv'
        from .../lib/resolv.rb:521:in `fetch_resource'
        from .../lib/resolv.rb:507:in `each_resource'
        from .../lib/resolv.rb:498:in `getresources'
        from (irb):2:in `<main>'

Current TXT records of google.com

$ dig txt google.com

; <<>> DiG 9.16.1-Ubuntu <<>> txt google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5232
;; flags: qr rd ra; QUERY: 1, ANSWER: 9, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;google.com.                    IN      TXT

;; ANSWER SECTION:
google.com.             3600    IN      TXT     "docusign=1b0a6754-49b1-4db5-8540-d2c12664b289"
google.com.             3600    IN      TXT     "facebook-domain-verification=22rm551cu4k0ab0bxsw536tlds4h95"
google.com.             3600    IN      TXT     "v=spf1 include:_spf.google.com ~all"
google.com.             3600    IN      TXT     "globalsign-smime-dv=CDYX+XFHUw2wml6/Gb8+59BsH31KzUr6c1l2BPvqKX8="
google.com.             3600    IN      TXT     "MS=E4A68B9AB2BB9670BCE15412F62916164C0B20BB"
google.com.             3600    IN      TXT     "apple-domain-verification=30afIBcvSuDV2PLX"
google.com.             3600    IN      TXT     "docusign=05958488-4752-4ef2-95eb-aa7ba8a3bd0e"
google.com.             3600    IN      TXT     "google-site-verification=wD8N7i1JTNTkezJ49swvWW48f8_9xveREV4oB-0Hf5o"
google.com.             3600    IN      TXT     "google-site-verification=TV9-DBe4R80X4v0M4U_bd_J9cpOJM0nikft0jAgjmsQ"

;; Query time: 48 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: 水  9月 08 15:29:15 JST 2021
;; MSG SIZE  rcvd: 625
hanazuki commented 7 months ago

I was unable to reproduce this error in the current master of resolv and Ruby 3.2.

The error is caused by a failure to read the length of TCP message so it looks related to TCP fallback, but is not the same problem as #6, which fails to decode the truncated UDP message.

hanazuki commented 1 month ago

As the dig result shows SERVER: 127.0.0.53#53(127.0.0.53), the direct nameserver seems to be systemd-resolved's stub listener. IIRC, it had bugs regarding truncation or TCP fallback in the past (I can't find the exact report or fix, but https://github.com/systemd/systemd/pull/6988 may be related).

The reported exception suggests that the TCP socket was closed before sending the message length, and resolv does't handle such a protocol violation. Here we need to check if read(2) actually returns 2 octets: https://github.com/ruby/resolv/blob/b25c63cf7cef2b298a47634fcb50ae1ec288d7df/lib/resolv.rb#L899-L903