dns
nimony/lib/std/dns.nim
const RTypeA: uint16const RTypeNs: uint16const RTypeCname: uint16const RTypePtr: uint16type Question = object name: string rtype: uint16 rclass: uint16
type Answer = object name: string rtype: uint16 ttl: uint32 ip4: string cname: string raw: string
type Message = object id: uint16 flags: uint16 questions: seq[Question] answers: seq[Answer]
proc encodeMessage(m: Message; s: var seq[char])Encode
mfor the wire, ready for a datagram. A response echoes its question verbatim and appends answers; the caller decides which of the two that is entirely by what lives inquestions/answers.proc decodeMessage(buf: openArray[char]; m: var Message): boolParse
bufintom, following compression in every name.falsefor anything a resolver cannot trust. The authority and additional sections are ignored.type Resolver = object hosts: ref StringTableObj not nil servers: seq[string]
proc initResolver(dl: Deadline): Resolverstd/dns's client: the machine's own hosts table wins over the network, which is asked at the nameservers the machine is configured with — thenameserverlines of/etc/resolv.confon POSIX,GetNetworkParamson Windows, which has no such file. Configuration that parses partially is used partially: a broken hosts file does not take the resolver down with it. The hosts file is read through the ring (asyncio), so construction is{.passive.}and bounded bydllike everything else here.proc query(r: var Resolver; host: string; serverIdx: int64; port: uint16; dl: Deadline): MessageOne question to
r.servers[serverIdx],port, and one datagram back, bounded bydl.resolvespeaks port 53; the port parameter exists so a caller can aim a question at an in-process server on an ephemeral port. Parsed but uninterpreted — the caller appliesresolve's rules. RaisesTimeoutErrorwhendlarrives before a reply to this question,NameNotFoundfor NXDOMAIN,IOErrorwhen no random id can be drawn.The id is drawn from the OS CSPRNG, and a reply counts only if it echoes the id and the question: with a clock-derived id and nothing else checked, an off-path attacker can guess well enough to have a forged answer accepted. A datagram that fails either check is dropped and the wait for the real one goes on, so a forgery cannot even make the query fail early.
proc resolve(r: var Resolver; hostIn: string; dl: Deadline): stringThe IPv4 dotted quad for
hostIn: from the machine's hosts table when it is listed there, from the loopback rule when it is alocalhostname, else from the first nameserver that answers. Follows CNAME chains inside the one response; raisesValueErrorfor a name the resolver cannot look up at all,TimeoutErrorwhen the budget runs out,NameNotFoundwhen the name is not known to the resolver or the zone it asked.proc resolve(name: string; dl: Deadline): stringResolve
nameusing the process-wide default resolver. The first call reads/etc/hostsand/etc/resolv.conf; every subsequent call reuses the same table. Raises the same errors as the explicitresolveform.type DnsQuestion = object req: ptr DnsRequest not nil name: string rtype: uint16 rclass: uint16
type DnsRequest = object sock: ptr UdpSocket id: uint16 flags: uint16 rcode: uint16 peer: PeerAddr questions: seq[DnsQuestion] answers: seq[Answer] buf: array[0..511, char]
type DnsServer = object sock: UdpSocket
proc newDnsServer(port: uint16; deadline: Deadline): DnsServerA bound, unconnected UDP socket able to answer questions.
portof 0 asks the kernel to pick one (boundPortunless the caller passes one). Passive, likeopenUdpit builds on: the socket, its flag and its bind go through the ring, so call this from a task, not a thread's top level.proc boundPort(s: var DnsServer): uint16proc close(s: var DnsServer)proc next(s: var DnsServer; r: var DnsRequest; dl: Deadline): boolThe next query to arrive, decoded into
rand ready to be answered.falseonce there will not be a next one, which is how a serve loop is told to stop:while s.next(r): ... r.respond()
Returns
falsewhendlarrives with no query, or when the receive fails — the same answeracceptgives a listener that stops serving. Never raises. A datagram that decodes to no question is dropped and the wait goes on.proc answer(q: DnsQuestion; name: string; rtype: uint16; ttl: uint32; data: string)Append one answer record to the request
qis part of.datais interpreted byrtype: a dotted quad forRTypeA, a dotted name for CNAME/PTR, rdata bytes verbatim for anything else — the three shapes a resolver reads back are the three this accepts.proc respond(r: var DnsRequest; dl: Deadline)Send the reply for
rto whoever asked: every question echoed, every answeranswercollected, the query's RD bit returned,rcodewhere a handler set it. Answers accumulate across the questions of one request, so the multi-A round-robin a resolver expects isq.answertwice in a row, not two procs for one shape.