Watch out for Time offsets in Ruby!
I got bitten by a silly bug in my dnsruby code recently - I thought I’d share it here in case anyone else starts pulling their hair out over this in future!
DNSSEC RRSIG records contain the signatures required to prove that a DNS zone has been correctly signed by an entity which possesses the correct keys for the zone. DNS clients can obtain the correct keys for the zone, then use the RRSIG records to prove that the zone (or record of interest) is correct. Of course, these records shouldn’t last forever - the data records are periodically re-signed (possibly with different keys), and the RRSIGs updated. The RRSIG includes an inception time, and an expiration time, to show the period over which it is valid. To verify a set of records, the DNS client must first produce an array of bytes, the digest of which is taken and used as the signature for the records - the salient data of of the RRSIG record (including the inception and expiration times) is included in this set of bytes.
The code I had written to do this was working fine - I had coded in the examples from the RFCs, and done a lot of work with actual signed zones (reading the data from the authoritative servers, and proving that it was correct). However, when I started to try to do this with real-world zone files, I started noticing that the signatures weren’t verifying. At least, they *sometimes* weren’t verifying. Very odd. Of course, with this kind of work, all you get is a Pass/Fail - no clue as to what is going wrong. I could see that the records were being translated to and from text format correctly - all the data in the RRs was showing as fine. However, when I compared the byte sequences prepared by my DNS client and Net::DNS, I noticed that four bytes were different. It turned out that, of the four byte sequences written to for the RRSIG inception and expiration time, there was a difference of 0xE100 - this works out to 16 hours worth of 3600 seconds. At last, I was onto something!
There are several ways to express time in the presentation format of the RRSIG record - “1234567890″ (seconds since 01/01/1970), or “YYYYMMDDHHMMSS” (e.g. “20090608123435″). Dnsruby worked fine with the first, and even translated the second from presentation format and back to presentation format correctly. However, when read using the following line :
return Time.mktime(year, mon, day, hour, min, sec).to_i
I got a 16 hour offset from the correct time! [When converting this time to/from the text format, the translation worked perfectly - it was only when inspecting the internal epoch time that the difference could be noticed]
I changed this to :
return Time.gm(year, mon, day, hour, min, sec).to_i
And suddenly everything worked just fine.
I’m sure that seasoned Rubyists will sneer at me for my stupidity - it did take me some time to track this one down! So, if you start noticing strange 16 hours offsets in your Ruby code, it’s worth checking your usage of the Time class…


June 11th, 2009 at 9:17 am
Time.mktime defaults to local time and Time.gm uses UTC, correct? If so, how did you happen to be working on a system with a 16 hour difference between localtime and UTC? I would have expected the difference to show up as 3600 seconds (between UTC and BST).
June 11th, 2009 at 9:25 am
Hi Geoff!
That’s a good question, and it is the reason I decided to blog about this experience. My laptop is correctly configured to run on (currently) British Summer Time. I have no clear answer as to why Ruby’s Time included a 16-hour offset when performing the conversion - it certainly surprised me!
June 11th, 2009 at 6:13 pm
That’s bizarre. BTW to my surprise the Ruby Net::DNS project seems to have recently crawled back to life: http://github.com/bluemonk/net-dns/
June 12th, 2009 at 6:28 am
Yes - I noticed there was a new release. However, as far as I can tell, there is still less functionality in that package than there is in the native resolv.rb Ruby standard library DNS implemention. I’m unclear as to why anyone would actually need to use it.