Repository navigation
DNS stub resolver (dmdns) + nslookup tool - #1
Merged
Merged
Conversation
dmdns resolves host names to IPv4/IPv6 addresses over the existing dmudp/dmip stack: - codec: RFC 1035 query build and response parse (compression pointers, CNAME chains, answer validated against ID and question) - address literal parse/format (IPv4, IPv6 incl. "::") - name servers: static list plus a dmdns_provide_servers DIF, so a DHCP client (or any other module) can supply servers without a link-time dependency in either direction - hosts table with built-in localhost, TTL cache with negative entries - dmdns_query(): one ephemeral UDP port and random ID per query - dmdns_resolve(): literal -> localhost -> hosts -> cache -> servers - tools/nslookup CLI - tests incl. real 8.8.8.8 answers and a DIF provider fixture module Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVi4u4Lf8HPw1emy5uyGSB
No buffer is sized from a protocol maximum any more - everything is allocated with Dmod_Malloc() at exactly the size it needs: - dmdns_build_query() computes the wire length and allocates it - dmdns_parse_response(), dmdns_query(), dmdns_resolve() and dmdns_get_servers() return address arrays of exactly the found size - dmdns_format_address() is replaced by dmdns_address_to_string() - the dmdns_provide_servers DIF reports servers through a callback instead of filling a caller-sized array - hosts/cache lookups return heap copies DMDNS_MAX_QUERY_LEN, DMDNS_MAX_MESSAGE_LEN, DMDNS_MAX_SERVERS, DMDNS_MAX_ADDRESSES and DMDNS_ADDRESS_STRLEN are gone; nslookup and the tests are back to the default 1024-byte stack. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVi4u4Lf8HPw1emy5uyGSB
configs/dns.ini (type=library) loads and enables dmdns at boot and disables/unloads it on "service stop dns", the same way dmicmp's icmp.ini does. It is a single system-wide unit without device rules: the resolver's state is global, and per-interface name servers are pulled through the dmdns_provide_servers DIF at lookup time. The unit ships in the release package via dmdns.dmr; docs/service.md describes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVi4u4Lf8HPw1emy5uyGSB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Until now the DMOD network stack had no way to turn a host name like
google.cominto an IP address (pingeven says so in its help text). This PR turns thedmdnstemplate into a DNS stub resolver (RFC 1034 5.3.1) that runs on top of the existingdmudp → dmip → dmnetbridge → dmnetifstack. It also adds:dnsdmsystem service that brings the resolver up at bootnslookupCLI toolArchitecture
Each layer can be used and tested on its own (
src/):dmdns_codec.cdmdns_build_query()/_parse_response()/_is_valid_name(). Pure functions, no network. Handles compression pointers (with a jump limit) and CNAME chains, and accepts an answer only if its ID and its question section match oursdmdns_address.cdmdns_parse_address()/_address_to_string()for IPv4 and IPv6 (including::, RFC 5952 output)dmdns_servers.cdmdns_provide_serversDIFdmdns_hosts.c/etc/hosts), with built-inlocalhostdmdns_cache.cdmdns_query.cdmudp_bind_any) and a random ID. The dmudp handler only copies the reply (at its real length) and posts a semaphore; parsing happens on the calling threaddmdns.cdmdns_resolve(): tries a literal, then localhost, hosts, cache, and finally the servers (2 rounds). Definitive answers are cachedService:
configs/dns.inidmdns is a Library module, so something has to load and enable it.
configs/dns.iniis a dmsystem unit for that:service start dnsloads and enables dmdns,service stop dnsdisables and unloads it. This is the same pattern as dmicmp'sicmp.ini.dmdns.dmr. With dmod-boot:dmdns service=dns.ini.It is one unit for the whole system, with no
[class=netif]rules, unlikenetworkd@anddhcp@:dns@eth0anddns@wlan0would share the same module, so stopping one of them would unload the resolver for all interfaces.See
docs/service.md.Memory: nothing is reserved up front
No buffer is sized from a protocol maximum (253-byte names, 512-byte UDP messages), and no thread's stack has to grow for dmdns. Everything is allocated with
Dmod_Malloc()at exactly its size, once that size is known:dmdns_build_query()computes the wire length first.dmdns_parse_response()counts the matching records, then allocates an array of exactly that size.Dmod_Realloc).dmdns_address_to_string()measures the text before allocating it.Results belong to the caller, who frees them with
Dmod_Free().nslookupand the tests run with the default 1024-byte stack.Key decision: name servers come in through a DIF
dmdnsowns no network configuration. The servers are known by whoever configured the interface: today that isdmdhcp, which already stores DHCP option 6 (dmdhcp_get_dns_server()); later it could be a modem driver or a static config module.dmdns_get_servers()merges the static list with every module that implements:A provider calls
add()once per server. There is no caller-sized array and no limit on the count.Providers are discovered on every lookup (
Dmod_GetNextDifModule), the same way dmip finds its protocol handlers. This gives:The full rationale is in
docs/dmdns.md.nslookup
[server]it goes throughdmdns_resolve(), the same path as any other module.[server]it asks only that server, viadmdns_query().Tests
test_dmdnscovers:www.github.comCNAME→A,ipv6.google.comCNAME→4×AAAA)dmdns_test_providerfixture module (a Library-type module, the same pattern dmip uses)test_nslookupruns the real tool throughDmod_RunModule().test_nslookup.Locally, both tests pass under
ctestwithdmf-getanddmod_loaderbuilt from dmoddevelop. I also checked that the release package containsconfigs/dns.ini. The unit itself has not been started under libsystemd on a board yet.Worth knowing
.data. If the DIF signature (const char* const) is defined in a different TU from the one that uses it, it ends up as an unrelocated pointer and segfaults. So it is defined indmdns_servers.c, the only file that uses it, as dmip and dmnetbridge already do.dmudp_send()gets an IPv6 path; AAAA records are still resolveddmdns_resolve()anddmdns_query()block, so they must not be called from a network receive callback.Proposed next steps (separate PRs)
dmdns_provide_serversfor leases in BOUND/RENEWING/REBINDING (there is a sketch indocs/dmdns.md). After that, DNS works on the board with no configuration.dmdns service=dns.iniandnslookuptoflash.dmd, and add a DNS responder totest_renode_network.py. This comes after the dmdns release.dmdns_resolve().🤖 Generated with Claude Code
https://claude.ai/code/session_01SVi4u4Lf8HPw1emy5uyGSB