DNS 位置记录 (2014)
DNS LOC Record (2014)

原始链接: https://blog.cloudflare.com/the-weird-and-wonderful-world-of-dns-loc-records/

Cloudflare 构建了自己的权威 DNS 服务器 RRDNS(用 Go 编写),以确保速度和抵御攻击的弹性。在实施这个基于 RFC 1035 等数十年标准复杂系统时,他们发现了一个关于一种罕见使用的 DNS 记录类型 LOC(位置)的错误。 尽管 Cloudflare 庞大的数据库中仅存在 743 条 LOC 记录,但有客户报告其中一条没有被正确提供。调查显示 RRDNS 可以*接收* LOC 请求并*创建*响应,但缺乏将人类可读的文本 LOC 格式(纬度、经度、海拔)转换为 RFC 1876 中定义的内部二进制格式的代码。 修复涉及编写一个解析器来处理这种转换,深入研究 LOC 记录的二进制编码的复杂性——该编码使用巧妙的位打包来优化大小和精度——并确保准确的数据表示。更新现已部署,所有 LOC 记录,包括 geekatlas.com 上的测试案例,都运行正常。这表明即使是很少使用的功能也需要在规模上进行细致的实现。

这个Hacker News讨论,源于一篇11年前关于DNS LOC记录的帖子(最初详细介绍了数据库中的743条记录),探讨了这个地理定位功能的实用性。LOC记录允许精确定位服务器的物理位置,可能有助于设备追踪。 用户们对当前LOC记录的使用规模感到好奇,并惋惜一个引用的示例网站已经消失。一位用户提议一个“自建服务器”项目,利用LOC记录追踪没有动态IP的个人设备,从而减轻精神负担。他们甚至考虑使用GPS数据进行动态更新,并指出Cloudflare之前已经增加了对此的支持。另一位用户则建议一个更简单的解决方案:通过HTTP提供位置数据,以文本文件形式。 这次对话突出了利用DNS进行位置服务的潜力,以及过去的挑战。
相关文章

原文

A cornerstone of CloudFlare's infrastructure is our ability to serve DNS requests quickly and handle DNS attacks. To do both those things we wrote our own authoritative DNS server called RRDNS in Go. Because of it we've been able to fight off DNS attacks, and be consistenly one of the fastest DNS providers on the web.

Implementing an authoritative DNS server is a large task. That's in part because DNS is a very old standard (RFC 1035 dates to 1987), in part because as DNS has developed it has grown into a more and more complex system, and in part because what's written in the RFCs and what happens in the real-world aren't always the same thing.

One little used type of DNS record is the LOC (or location). It allows you to specify a physical location. CloudFlare handles millions of DNS records; of those just 743 are LOCs. Nevertheless, it's possible to set up a LOC record in the CloudFlare DNS editor.

Trinity

My site geekatlas.com has a LOC record as an Easter Egg. Here's how it's configured in the CloudFlare DNS settings:

LOC Example

When you operate at CloudFlare scale the little-used nooks and crannies turn out to be important. And even though there are only 743 LOC records in our entire database, at least one customer contacted support to find out why their LOC record wasn't being served.

And that sent me into the RRDNS source code to find out why.

The answer was simple. Although RRDNS had code for receiving requests for LOC records, creating response packets containing LOC data, there was a missing link. The CloudFlare DNS server stores the LOC record as a string (such as the 33 40 31 N 106 28 29 W 10m above) and no one had written the code to parse that and turn it into the internal format. Oops.

The textual LOC format and the binary, on-the-wire, format are described in RFC 1876 and it's one of many RFCs that updated the original 1987 standard. RFC 1876 is from 1996.

The textual format is fairly simple. Here's what the RFC says:

The LOC record is expressed in a primary file in the following format:

owner TTL class LOC ( d1 [m1 [s1]] {"N"|"S"} d2 [m2 [s2]]
                           {"E"|"W"} alt["m"] [siz["m"] [hp["m"]
                           [vp["m"]]]] )

where:

   d1:     [0 .. 90]            (degrees latitude)
   d2:     [0 .. 180]           (degrees longitude)
   m1, m2: [0 .. 59]            (minutes latitude/longitude)
   s1, s2: [0 .. 59.999]        (seconds latitude/longitude)
   alt:    [-100000.00 .. 42849672.95] BY .01 (altitude in meters)
   siz, hp, vp: [0 .. 90000000.00] (size/precision in meters)

If omitted, minutes and seconds default to zero, size defaults to 1m,
horizontal precision defaults to 10000m, and vertical precision
defaults to 10m.  These defaults are chosen to represent typical
ZIP/postal code area sizes, since it is often easy to find
approximate geographical location by ZIP/postal code.

So, there are required latitude, longitude and altitude and three optional values for the size of the location and precision information. Pretty simple.

Then there's the on-the-wire format. Unlike a TXT record the LOC record data is parsed and turned into a fixed size binary format. Back to RFC 1876:

  MSB                                           LSB
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  0|        VERSION        |         SIZE          |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  2|       HORIZ PRE       |       VERT PRE        |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  4|                   LATITUDE                    |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  6|                   LATITUDE                    |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  8|                   LONGITUDE                   |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 10|                   LONGITUDE                   |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 12|                   ALTITUDE                    |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 14|                   ALTITUDE                    |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

So, 32 bits of latitude, longitude and altitude and then three 8 bit values for the size and precision. The latitude and longitude values have a pretty simple encoding that treats the 32 bits as an unsigned integer:

The latitude of the center of the sphere described by the SIZE field, expressed as a 32-bit integer, most significant octet first (network standard byte order), in thousandths of a second of arc.  2^31 represents the equator; numbers above that are north latitude.

And the altitude can be below sea-level but still unsigned:

The altitude of the center of the sphere described by the SIZE field, expressed as a 32-bit integer, most significant octet first (network standard byte order), in centimeters, from a base of 100,000m below the [WGS 84] reference spheroid used by GPS.

But the 8 bit values use a very special encoding that allows a wide range of approximate values to be packed into 8 bits and also be human-readable when dumped out in hex!

The diameter of a sphere enclosing the described entity, in centimeters, expressed as a pair of four-bit unsigned integers, each ranging from zero to nine, with the most significant four bits representing the base and the second number representing the power of ten by which to multiply the base.  This allows sizes from 0e0 (<1cm) to 9e9 (90,000km) to be expressed.  This representation was chosen such that the hexadecimal representation can be read by eye; 0x15 = 1e5.

For example, the value 0x12 means 1 * 10^2 or 100cm. 0x99 means 9 * 10^9 or 90,000,000m. The smallest value that can be represented is 1cm (it's 0x10). So, in just 8 bits there's a range values from 1cm to larger than the diameter of Jupiter.

To fix this I wrote a parser for the LOC text record type (and associated tests). It can be found here.

We've now rolled out the fix and all the existing LOC records are being served by RRDNS. For example, my geekatlas.com LOC record can be queried like this:

$ dig geekatlas.com LOC
; <<>> DiG 9.8.3-P1 <<>> geekatlas.com LOC
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2997
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:    
;geekatlas.com.         IN  LOC

;; ANSWER SECTION:
geekatlas.com.      299 IN  LOC 33 40 31.000 N 106 28 29.000 W 10.00m 1m 10000m 10m

;; Query time: 104 msec
;; SERVER: 192.168.14.1#53(192.168.14.1)
;; WHEN: Tue Apr  1 14:13:48 2014
;; MSG SIZE  rcvd: 59
联系我们 contact @ memedata.com