Re: Factor

Factor: the language, the theory, and the practice.

DNS LOC Records

Wednesday, December 10, 2025

#networking

DNS is the Domain Name System and is the backbone of the internet:

Most prominently, it translates readily memorized domain names to the numerical IP addresses needed for locating and identifying computer services and devices with the underlying network protocols. The Domain Name System has been an essential component of the functionality of the Internet since 1985.

It is also an oft-cited reason for service outages, with a funny decade-old r/sysadmin meme:

Factor has a DNS vocabulary that supports querying and parsing responses from nameservers:

IN: scratchpad USE: tools.dns

IN: scratchpad "google.com" host
google.com has address 142.250.142.113
google.com has address 142.250.142.138
google.com has address 142.250.142.100
google.com has address 142.250.142.101
google.com has address 142.250.142.102
google.com has address 142.250.142.139
google.com has IPv6 address 2607:f8b0:4023:1c01:0:0:0:8b
google.com has IPv6 address 2607:f8b0:4023:1c01:0:0:0:8a
google.com has IPv6 address 2607:f8b0:4023:1c01:0:0:0:64
google.com has IPv6 address 2607:f8b0:4023:1c01:0:0:0:65
google.com mail is handled by 10 smtp.google.com

Recently, I bumped into an old post on the Cloudflare blog about The weird and wonderful world of DNS LOC records and realized that we did not properly support parsing RFC 1876 which specifies a format for returning LOC or location record specifying the physical location of a service.

At the time of the post, Cloudflare indicated they handle “millions of DNS records; of those just 743 are LOCs.”. I found a webpage that lists sites supporting DNS LOC and contains only nine examples.

It is not widely used, but it is very cool.

You can use the dig command to query for a LOC record and see what is returned:

$ dig alink.net LOC
alink.net.              66      IN      LOC     37 22 26.000 N 122 1 47.000 W 30.00m 30m 30m 10m

The fields that were returned include:

  • latitude (37° 22’ 26.00" N)
  • longitude (122° 1’ 47.00" W)
  • altitude (30.00m)
  • horizontal precision (30m)
  • vertical precision (30m)
  • entity size estimate (10m)

In Factor 0.101, the field is available and returned as bytes but not parsed:

IN: scratchpad "alink.net" dns-LOC-query answer-section>> ...
{
    T{ rr
        { name "alink.net" }
        { type LOC }
        { class IN }
        { ttl 300 }
        { rdata
            B{
                0 51 51 19 136 5 2 80 101 208 181 8 0 152 162 56
            }
        }
    }
}

Of course, I love odd uses of technology like Wikipedia over DNS and I thought Factor should probably add proper support for the LOC record!

First, we define a tuple class to hold the LOC record fields:

TUPLE: loc size horizontal vertical lat lon alt ;

Next, we parse the LOC record, converting sizes (in centimeters), lat/lon (in degrees), and altitude (in centimeters):

: parse-loc ( -- loc )
    loc new
        read1 0 assert=
        read1 [ -4 shift ] [ 4 bits ] bi 10^ * >>size
        read1 [ -4 shift ] [ 4 bits ] bi 10^ * >>horizontal
        read1 [ -4 shift ] [ 4 bits ] bi 10^ * >>vertical
        4 read be> 31 2^ - 3600000 / >>lat
        4 read be> 31 2^ - 3600000 / >>lon
        4 read be> 10000000 - >>alt ;

We hookup the LOC type to be parsed properly:

M: LOC parse-rdata 2drop parse-loc ;

And then build a word to print the location nicely:

: LOC. ( name -- )
    dns-LOC-query answer-section>> [
        rdata>> {
            [ lat>> [ abs 1 /mod 60 * 1 /mod 60 * ] [ neg? "S" "N" ? ] bi ]
            [ lon>> [ abs 1 /mod 60 * 1 /mod 60 * ] [ neg? "W" "E" ? ] bi ]
            [ alt>> 100 / ]
            [ size>> 100 /i ]
            [ horizontal>> 100 /i ]
            [ vertical>> 100 /i ]
        } cleave "%d %d %.3f %s %d %d %.3f %s %.2fm %dm %dm %dm\n" printf
    ] each ;

And, finally, we can give it a try!

IN: scratchpad "alink.net" LOC.
37 22 26.000 N 122 1 47.000 W 30.00m 30m 30m 10m

Yay, it matches!

This is available in the latest development version.