~/bend-docscommunity

dns.bend relies on unsafe/foreign

raw source on the hub · import bend-kit-dns@0.5.0.0/dns.bend as Dns

DNS codec and host lookup. Source: https://github.com/paymog/bend-kit/tree/main/dns

3 imports
import Base
import bend-kit-wire@0.4.3.0/wire.bend as Wire
import bend-kit-bytes@0.3.1.0/bytes.bend as Bytes

Types

type Step source · line 85 · raw

Data

One step through a name: go on at the next label, or stop with the offset past the name.

type RR source · line 118 · raw

Data

type Try source · line 380 · raw

Data

One attempt's outcome: try again, or this answer (None: no address).

Definitions

def label.ok source · line 13 · raw

@+l:String -> Bool

def label.ascii source · line 17 · raw

@s:String -> Bool

def labels.ok source · line 24 · raw

@xs:List<&2, String> -> Bool

def labels.len source · line 32 · raw

@xs:List<&2, String> -> @+n:U32 -> U32

QNAME's length without its final zero octet.

def label.put source · line 39 · raw

@s:String -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @+i:U32 -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes

def labels.put source · line 47 · raw

@xs:List<&2, String> -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @+i:U32 -> 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes

Each label as its length octet, then its octets. The zero octet that ends QNAME is already there.

def query.if source · line 55 · raw

@+id:U32 -> @+xs:List<&2, String> -> @ok:Bool -> Maybe<&1, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes>

def query.labels source · line 64 · raw

@+id:U32 -> @+xs:List<&2, String> -> Maybe<&1, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes>

def strip_dot.if source · line 67 · raw

@+s:String -> @dot:Bool -> String

def strip_dot source · line 74 · raw

@+s:String -> String

def query source · line 78 · raw

@id:U32 -> @name:String -> Maybe<&1, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes>

RFC 1035 §4.1: a standard query for the A record of name.

def name.step.of source · line 90 · raw

@+i:U32 -> @+l:U32 -> Step

§4.1.4: l, the octet at i, is 0 at the end, 192..255 for a two-octet pointer, 64..191 reserved.

def name.step source · line 93 · raw

@+i:U32 -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Step)

def name.walk source · line 101 · raw

@fuel:Nat -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Step) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>)

def name.skip source · line 115 · raw

@b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @+i:U32 -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>)

The offset past the name at i. §2.3.4: a name is at most 255 octets, so 128 steps are enough.

def dotted source · line 123 · raw

@+ip:U32 -> String

def rr.a source · line 126 · raw

@r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, RR)

def rr.data source · line 135 · raw

@a:Bool -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @+i:U32 -> @+len:U32 -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, RR)

RDATA at i holds len octets. A skipped record past the end fails at the next read.

def rr.len source · line 142 · raw

@+o:U32 -> @+ty:U32 -> @+cl:U32 -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, RR)

def rr.cl source · line 150 · raw

@+o:U32 -> @+ty:U32 -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, RR)

def rr.ty source · line 158 · raw

@+o:U32 -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, RR)

def rr.name source · line 166 · raw

@r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, RR)

def rr source · line 175 · raw

@b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @+i:U32 -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, RR)

§4.1.3: NAME, TYPE, CLASS, TTL, RDLENGTH, RDATA.

def answers.scan source · line 179 · raw

@fuel:Nat -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, RR) -> Maybe<&2, String>

fuel = records left. The first A record wins; CNAMEs before it are skipped.

def question.end source · line 194 · raw

@r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>)

QTYPE and QCLASS follow the name.

def questions source · line 202 · raw

@fuel:Nat -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>)

def body.go source · line 215 · raw

@+an:U32 -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Maybe<&2, String>

def body.an source · line 224 · raw

@+qd:U32 -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Maybe<&2, String>

The 12-octet header ends with NSCOUNT and ARCOUNT; the questions follow.

def body.qd source · line 232 · raw

@r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Maybe<&2, String>

def flags.ok source · line 241 · raw

@+f:U32 -> Bool

§4.1.1: QR set, opcode QUERY, not truncated, RCODE 0.

def head.flags.if source · line 244 · raw

@ok:Bool -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Maybe<&2, String>

def head.flags source · line 251 · raw

@r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Maybe<&2, String>

def head.id.if source · line 259 · raw

@ok:Bool -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Maybe<&2, String>

def head.id source · line 266 · raw

@+id:U32 -> @r:Pair(0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes, Maybe<&2, U32>) -> Maybe<&2, String>

def answer source · line 275 · raw

@id:U32 -> @msg:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> Maybe<&2, String>

The IPv4 address the response gives for the query with this id.

def nameservers.add source · line 281 · raw

@+line:String -> @rest:List<&2, String> -> @hit:Bool -> List<&2, String>

def nameservers.go source · line 288 · raw

@xs:List<&2, String> -> List<&2, String>

def nameservers source · line 296 · raw

@conf:String -> List<&2, String>

Every nameserver line, in file order.

def nameserver.first source · line 299 · raw

@xs:List<&2, String> -> Maybe<&2, String>

def nameserver source · line 307 · raw

@conf:String -> Maybe<&2, String>

The first nameserver line of a resolv.conf.

def none source · line 313 · raw

IO(Maybe<&2, String>)

def list.append source · line 316 · raw

@xs:List<&2, String> -> @ys:List<&2, String> -> List<&2, String>

def list.one source · line 323 · raw

@+x:String -> List<&2, String>

def list.any source · line 326 · raw

@+xs:List<&2, String> -> Bool

def list.first source · line 333 · raw

@xs:List<&2, String> -> Maybe<&2, String>

def ipv4.go source · line 340 · raw

@s:String -> Bool

def ipv4 source · line 348 · raw

@+s:String -> Bool

Digits and dots; TCP.connect rejects anything else that is not an address.

def ipv6.ch source · line 351 · raw

@+c:U32 -> Bool

def ipv6.go source · line 358 · raw

@s:String -> Bool

def ipv6.has source · line 365 · raw

@s:String -> Bool

def ipv6 source · line 373 · raw

@+s:String -> Bool

Bracket-free IPv6 text; connect validates the address.

def ip source · line 376 · raw

@+s:String -> Bool

def try.pure source · line 384 · raw

@s:Socket -> @t:Try -> IO(Pair(Socket, Try))

def try.from source · line 388 · raw

@s:Socket -> @+ns:String -> @id:U32 -> @hpd:Pair(String, Pair(U32, Pair(U32, Array<U32>))) -> IO(Pair(Socket, Try))

A datagram from anyone but the nameserver's port 53 is ignored (retried).

def try.back source · line 393 · raw

@ns:String -> @id:U32 -> @m:Pair(Socket, Result<&1, &1, Pair(U32, String), Pair(String, Pair(U32, Pair(U32, Array<U32>)))>) -> IO(Pair(Socket, Try))

def try.sent source · line 402 · raw

@ns:String -> @id:U32 -> @m:Pair(Socket, Result<&1, &1, Pair(U32, String), Unit>) -> IO(Pair(Socket, Try))

§4.2.1 and resolv.conf defaults: 5 s per attempt.

def try.send source · line 413 · raw

@s:Socket -> @+ns:String -> @id:U32 -> @q:Maybe<&1, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes> -> IO(Pair(Socket, Try))

Bytes cannot be copied, so each attempt builds its own query. A name that query rejects ends the tries.

def try.once source · line 422 · raw

@s:Socket -> @+ns:String -> @+id:U32 -> @name:String -> IO(Pair(Socket, Try))

def try.close source · line 425 · raw

@s:Socket -> @r:Maybe<&2, String> -> IO(Maybe<&2, String>)

def tries.end source · line 430 · raw

@st:Pair(Socket, Try) -> IO(Maybe<&2, String>)

def tries source · line 439 · raw

@fuel:Nat -> @+ns:String -> @+id:U32 -> @+name:String -> @st:Pair(Socket, Try) -> IO(Maybe<&2, String>)

fuel: attempts left (resolv.conf's default is 2).

def resolve.sock source · line 453 · raw

@ns:String -> @id:U32 -> @name:String -> @r:Result<&1, &1, Pair(U32, String), Socket> -> IO(Maybe<&2, String>)

def resolve.id source · line 461 · raw

@name:String -> @ns:String -> @r:Result<&1, &1, Pair(U32, String), U32> -> IO(Maybe<&2, String>)

§7.3: a random id makes forged answers harder to land.

def resolve.ns source · line 470 · raw

@name:String -> @ns:Maybe<&2, String> -> IO(Maybe<&2, String>)

def resolve.at source · line 480 · raw

@+host:String -> @ns:String -> IO(Maybe<&2, String>)

Ask one nameserver. Tests use this; resolve reads /etc/resolv.conf.

def conf.text source · line 483 · raw

@r:Result<&1, &1, Pair(U32, String), String> -> String

def resolve.n3 source · line 491 · raw

@+name:String -> @xs:List<&2, String> -> IO(Maybe<&2, String>)

ponytail: 3 nameservers; a longer resolv.conf ignores the rest

def resolve.n2b source · line 498 · raw

@+name:String -> @rest:List<&2, String> -> @ip:Maybe<&2, String> -> IO(Maybe<&2, String>)

def resolve.n2 source · line 505 · raw

@+name:String -> @xs:List<&2, String> -> IO(Maybe<&2, String>)

def resolve.n1b source · line 514 · raw

@+name:String -> @rest:List<&2, String> -> @ip:Maybe<&2, String> -> IO(Maybe<&2, String>)

def resolve.list source · line 521 · raw

@+name:String -> @xs:List<&2, String> -> IO(Maybe<&2, String>)

def resolve.read source · line 530 · raw

@name:String -> @m:Pair(File, Result<&1, &1, Pair(U32, String), String>) -> IO(Maybe<&2, String>)

def resolve.conf source · line 536 · raw

@name:String -> @r:Result<&1, &1, Pair(U32, String), File> -> IO(Maybe<&2, String>)

def resolve.dns source · line 545 · raw

@name:String -> IO(Maybe<&2, String>)

def hosts.on_line source · line 551 · raw

@+name:String -> @ns:List<&2, String> -> Bool

def hosts.line.all source · line 558 · raw

@+name:String -> @xs:List<&2, String> -> List<&2, String>

def hosts.sp source · line 565 · raw

@+c:U32 -> @tab:Bool -> U32

def hosts.flat source · line 572 · raw

@s:String -> String

def hosts.scan.all source · line 579 · raw

@xs:List<&2, String> -> @+name:String -> @acc:List<&2, String> -> List<&2, String>

def hosts.all source · line 588 · raw

@+name:String -> @text:String -> List<&2, String>

Every address for name in an /etc/hosts file. The name is already lowercase.

def hosts source · line 592 · raw

@+name:String -> @text:String -> Maybe<&2, String>

First address for name in an /etc/hosts file. The name is already lowercase.

def lookup.split source · line 595 · raw

@+s:String -> List<&2, String>

def resolve.literal.all source · line 604 · raw

@+host:String -> List<&2, String>

Literals and localhost without the OS (for laws and fast paths).

def resolve.literal source · line 609 · raw

@+host:String -> Maybe<&2, String>

def resolve.all.got source · line 612 · raw

@r:Result<&1, &1, Pair(U32, String), String> -> IO(List<&2, String>)

def resolve.all.os source · line 619 · raw

@+name:String -> IO(List<&2, String>)

def resolve.all.dns.one source · line 624 · raw

@m:Maybe<&2, String> -> IO(List<&2, String>)

def resolve.all.dns source · line 632 · raw

@+name:String -> IO(List<&2, String>)

ponytail: UDP path still asks for A records only

def resolve.all.from source · line 637 · raw

@+name:String -> @+xs:List<&2, String> -> IO(List<&2, String>)

def resolve.all.text source · line 644 · raw

@+name:String -> @r:Result<&1, &1, Pair(U32, String), String> -> IO(List<&2, String>)

def resolve.all.opened source · line 651 · raw

@+name:String -> @m:Pair(File, Result<&1, &1, Pair(U32, String), String>) -> IO(List<&2, String>)

def resolve.all.hosts source · line 657 · raw

@+name:String -> @r:Result<&1, &1, Pair(U32, String), File> -> IO(List<&2, String>)

def resolve.all.pick source · line 666 · raw

@+host:String -> @+xs:List<&2, String> -> IO(List<&2, String>)

def resolve.all source · line 673 · raw

@+host:String -> IO(List<&2, String>)

def resolve.all.ask source · line 676 · raw

@+name:String -> IO(List<&2, String>)

def resolve.all.pure.pick source · line 681 · raw

@+host:String -> @+xs:List<&2, String> -> IO(List<&2, String>)

def resolve.all.pure source · line 688 · raw

@+host:String -> IO(List<&2, String>)

def resolve.pure source · line 691 · raw

@+host:String -> IO(Maybe<&2, String>)

def resolve source · line 697 · raw

@+host:String -> IO(Maybe<&2, String>)

First address from resolve.all (getaddrinfo order when the OS resolves).

Effects (foreign code)

effect lookup.all source · line 599 · raw

@host:String -> IO(Result<&1, &1, Pair(U32, String), String>)

OS resolver: every address, NUL-separated in getaddrinfo order.

foreign: effs/dns.c, effs/dns.js