cripslock

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

20260102_Magic_DNS.md (8574B)


      1 Topics: rust esp32 eculocate
      2 Subject: Magic DNS
      3 Date: Fri Jan  2 10:58:45 2026
      4 
      5 It's all very 2026 around here, isn't it? I am reminded by
      6 
      7 * [The Case for Blogging in the Ruins](https://www.joanwestenberg.com/the-case-for-blogging-in-the-ruins/)  and
      8 * [A website to destroy all websites](https://henry.codes/writing/a-website-to-destroy-all-websites/)
      9 
     10 to jot down some of what I've been doing in the past month or so. The
     11 tl;dr is "making a thing I can plug into my motorbike ECU to log the
     12 data (rpm, speed, throttle position, temperatures etc etc) it
     13 produces". For reasons mostly of ramifying the learning opportunities,
     14 I decided the best way would be to get a cheap ESP-32 device (it's
     15 RISC-V - isn't that cool?) and hook it up to a level converter, and
     16 then write a program for it in Rust (Rust learning opportunity ahoy)
     17 to twiddle the serial line appropriately and send the data over the
     18 network to the mobile phone which sits on my handlebars.
     19 
     20 It turns out that I spent _way_ less time getting the serial interface
     21 to the [Honda K-line ECU signal](https://gonzos.net/projects/ctx-obd/)
     22 to reveal its secrets than on the "why don't you just ..." part where
     23 I want to stream the data over wifi to another device. So this post is
     24 actually not at all about hardware hacking.
     25 
     26 The constraints I have imposed on myself here are
     27 
     28 * I do not want to hardcode my wifi ssid and password into the device
     29 (actually, I have at least two wifi networks I may want to use it with)
     30 
     31 * the (yet to be written) data collection app should be able to find
     32 the device without hardcoding its IP address or requiring me to type it
     33 in - as the device is getting its address from DHCP, we don't even
     34 know what address it will get
     35 
     36 These are both _in principle_ solved problems.
     37 
     38 There's a convention for [provisioning wifi on these devices](https://randomnerdtutorials.com/esp32-wi-fi-provisioning-ble-arduino/)
     39 which involves using a mobile phone app to connect to it using BLE
     40 then sending the ssid/password of the chosen access point.  In fact
     41 there's even a prebuilt Android app which we can use and an esp32
     42 arduino library which we can't (because we have elected to make our
     43 lives difficult and use Rust instead). But I am led to believe that
     44 "rewrite everything in Rust" is idiomatic for Rust programmers anyway.
     45 I haven't done this yet.
     46 
     47 And for the "what's my IP address" problem there is a standard way, by
     48 combining [Multicast DNS](https://datatracker.ietf.org/doc/html/rfc6762)
     49 and [DNS-based Service Discovery](https://datatracker.ietf.org/doc/html/rfc6763), for
     50 computers to publish their services on the LAN. When I say
     51 "computers": if this household is typical, mostly they're set-top
     52 boxes, printers, light bulbs, smart speakers and thermostats rather
     53 than general-purpose computing devices. I've mostly done this bit.
     54 
     55 ## Terms
     56 
     57 **Multicast DNS** is DNS, but peer-to-peer: it reuses mostly the same packet formats
     58 but instead of requiring a centralised server which knows all the
     59 names, every device listens on a multicast address
     60 for DNS queries for its _own_ name.
     61 
     62 **DNS-SD** is a convention for which records you can query/need to send in
     63 order to advertise what kind of services you have and where they are.
     64 Because sending an A record alone is not sufficient for anyone with a
     65 Mac and a fancy-schmancy service browser to know what _kind_ of service
     66 is on offer at that address. Is it a printer? A dishwasher? An
     67 IoT air fryer?
     68 
     69 The RFCs for each (which are, by the way, much easier reads than a lot
     70 of RFCs and contain no EBNF at all) go to great lengths to point out
     71 that each is independent of the other. But they stack well.
     72 
     73 ## DNS-SD, 3048 metre view
     74 
     75 DNS-SD is based on a paradigm of "services" and "service instances". A
     76 "service" is the general "kind" of thing on offer and is named
     77 something like `_http._tcp.local` - it will always end in `_tcp.local`
     78 if it is TCP or `_udp.local` if it is anything other than TCP. For our
     79 ECU project we chose the service name `_keihin._udp.local` after the
     80 manufacturer of the ECUs that the device knows how to talk to.  A
     81 service instance might be something like
     82 `WiserHeat05AB12._http._tcp.local`.  Service names aren't usually
     83 hierarchical but there are a few with a second level like
     84 `_printer._sub._http._tcp`
     85 
     86 
     87 The minimum/usual set of records you need to publish for DNS-SD is
     88 this (pseudocode)
     89 
     90 ```
     91 myinstancename._theservicename._udp.local SRV, data: (target: myhostname.local, port: nnnn)
     92 myhostname.local A, data: a.b.c.d
     93 myinstancename._theservicename._udp.local TXT, data: "txtvers=1"
     94 _theservicename._udp.local PTR, data:  myinstancename._theservicename._udp.local
     95 _services._udp.local PTR, data:  _theservicename._udp.local
     96 ```
     97 
     98 Your service instance needs a SRV and a TXT, then there's a PTR
     99 connecting the service instance to the service for people who are
    100 browsing the service - think about e.g. an "Add a printer" dialog box,
    101 then there's a PTR from `_services._udp.local` to the service name PTR
    102 for people who are running `avahi-browse -a` or its moral equivalent
    103 in GUI-land. And not forgetting there's an A record matching the one in the
    104 SRV record data.
    105 
    106 * Note: `_services._udp.local` is the right name for discovery
    107 even if your service is TCP - there is no `_services._tcp.local`
    108 
    109 * Note: I am assuming `.local` is the suffix, which is likely true for
    110 MDNS but probably not if you are using DNS-SD with regular DNS
    111 
    112 ## MDNS
    113 
    114 The single biggest problem when implementing MDNS is the lack of
    115 tooling to test it against. In my experience:
    116 
    117 * dig: historically, you used to be able to `dig @224.0.0.251 -p 5353 name.local` but it largely worked by accident and [now it
    118 doesn't](https://gitlab.isc.org/isc-projects/bind9/-/issues/3428). Note
    119 that even when it _did_ work it wasn't sending the same packets as a
    120 real MDNS query.
    121 
    122 * avahi-browse: e.g. `avahi-browse -v -a` shows all the services on
    123   the LAN. Note that is is a frontend to the persistent `avahi-daemon`
    124   and there is caching happening in there somewhere, so if it didn't
    125   work then and you made a change and restarted your service ... is
    126   your service still broken or did it not reissue the query? shrug-emoji.gif
    127 
    128 * `mquery` from Jeremie Miller's mdnsd: note that this has been forked
    129   into a million pieces. The one I'm using is
    130   [https://github.com/troglobit/mdnsd](https://github.com/troglobit/mdnsd).
    131   This works for querying the `_services._udp.local` discovery name
    132   but if you run e.g.  `mquery _scanner._tcp.local` it appears to send
    133   the query and then sit there silently ignoring the responses. *But*:
    134   it doesn't cache as avahi-browse does, so that's good.
    135 
    136 * wireshark, of course. Wireshark is pretty good, but note that it
    137   will display your replies as "Unsolicited" because the query id for
    138   MDNS is (per the standard) 0, so there is no way for it to correlate
    139   them with requests.
    140 
    141 * [mdns-debugger](https://github.com/andrewbonney/mdns-debugger) was
    142   handy for pointing out my TTLs (and a lot of other TTLs) were wrong.
    143   It didn't point out that my PTR record data was incorrectly encoded
    144   and was therefore naming a nonexistent A record, which was a source
    145   of much hair tearing.
    146 
    147 * there are a couple of Android apps I also used, mostly to see what
    148   they'd do when nothing was working (see "hair tearing" above) and I
    149   wa sout of ideas. "mDNS Discovery" (`com.mdns_discovery.app`) and
    150   "Service Browser" (com.druk.servicebrowser).  The first one is
    151   prettier, the second one _very_ helpfully rendered the errant PTR as
    152   with a backslash as `eculogical\.local` and so led me to the said
    153   encoding error.
    154 
    155 
    156 ## Where are we now?
    157 
    158 I believe that it now does everything an mdns responder SHOULD(sic) do
    159 except
    160 
    161 * compress labels in response data
    162 * ignore queries for records where the record we'd send is already in the answers section of the query message
    163 * NSEC
    164 
    165 and I can't decide, in the context of this being a program that
    166 probably nobody else in the world will ever use and even I will only
    167 use on one single piece of hardware (I only have one motorbike)
    168 whether implementing those things is a good and laudable decision
    169 because spec compliance is important, or just a way of further putting
    170 off the inevitable next step which involves writing the Android app
    171 to collect the data.
    172 
    173 It also could do with being extracted into its own module/crate/thing
    174 to be more modular. I'd say "to aid reuse" but I don't think anyone
    175 really wants to (or should want to) reuse my [novice-level Rust code](https://gti.telent.net/dan/eculogical/src/branch/main/ecu-esp32/src/wifi.rs).
    176 Learning in public.