CheckAllDNSScheduledTask - undefined method 'unpack' for nil (NoMethodError) len = readable_socks[0].read(2).unpack('n')[0]
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 68/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- ruby
- Domain
- backend, networking
Research direction
Start at CheckAllDNSScheduledTask and inspect the project's resolv dependency, then review Resolv::DNS::Requester::TCP#recv_reply and the Resolv::DNS#fetch_resource fallback described in the issue. Update resolv from 0.6.2 to 0.7.1 and verify the scheduled DNS checks no longer surface the nil.unpack error.
Written by the indexing model from the issue text.
Description
Describe the bug
Sometimes, when CheckAllDNSScheduledTask is running, we see the following error:
undefined method 'unpack' for nil (NoMethodError) len = readable_socks[0].read(2).unpack('n')[0]
To Reproduce
This is hard, I guess have a lot of domains that need to do a DNS check?
Explanation and suggested solution from Claude:
The failing line lives in Resolv::DNS::Requester::TCP#recv_reply (resolv 0.6.2, line 931):
def recv_reply(readable_socks)
len = readable_socks[0].read(2).unpack('n')[0] # <-- nil.unpack here
reply = @socks[0].read(len)
return reply, nil
end
DNS-over-TCP frames every message with a 2-byte big-endian length prefix, which is what read(2).unpack('n') is decoding. recv_reply is only called after the requester's request loop has done an IO.select/wait_readable and been told the socket is readable. The catch: a socket whose peer has closed the connection also reports as readable, and IO#read(2) returns nil at EOF rather than blocking. So when the nameserver (or a firewall/middlebox) closes the TCP connection without sending a complete reply, read(2) is nil and nil.unpack('n') raises your NoMethodError.
Why it only happens sometimes, and via the DNS task. Resolv only uses TCP as a fallback. Looking at Resolv::DNS#fetch_resource, it queries over UDP first and only retries over TCP when the UDP reply comes back with the truncation bit set (reply.tc == 1). This stdlib path doesn't negotiate a large EDNS0 buffer, so truncation — and thus the TCP fallback — kicks in for domains with big record sets: lots of TXT (heavy SPF/DKIM/verification records), many MX or NS, DNSSEC, etc. CheckAllDNSScheduledTask walks TXT/MX/CNAME/NS across every domain and track-domain, so it's the task most likely to hit a truncated response, fall back to TCP, and then meet a nameserver that drops the TCP connection. That's your "sometimes."
Why 0.6.2 turns it into a crash. The request loop does guard the socket read — but only for Errno::ECONNREFUSED and Errno::ECONNRESET, which it converts to a timeout. A clean FIN (graceful close → read returns nil) isn't an exception, so it slips past that rescue and surfaces as the raw NoMethodError.
The fix. Upstream hardened exactly this. The first released version containing the fix is resolv 0.7.1, where recv_reply now does:
len_data = readable_socks[0].read(2)
raise EOFError if len_data.nil? || len_data.bytesize != 2
len = len_data.unpack('n')[0]
reply = @socks[0].read(len)
raise EOFError if reply.nil? || reply.bytesize != len
So the practical fix is to update the gem to 0.7.1.
- Dominant language
- Ruby
- Stars
- 16.8k
- Forks
- 1.3k
- Avg merge
- 13d 7h
- Merged PRs (30d)
- 2
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from postalserver/postal
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
postalserver/postal#3567 ·
-
bug
Difficulty 1/5 Under an hour Newbie friendliness 75/100
postalserver/postal#3562 · 2 comments ·
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 55/100
postalserver/postal#3620 · 1 comment ·
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 58/100
postalserver/postal#3595 · 1 comment ·
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 55/100
postalserver/postal#3592 · 2 comments ·
All issues in postalserver/postal
Similar issues
-
user-reported
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Kong/developer.konghq.com#7316 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
TheOdinProject/curriculum#31408 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
notch8/utk_knapsack#148 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
Homebrew/homebrew-cask#288729 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100