ref: e0f3c2226a618f63776061b3629d97c49679702b
dir: /libstd/resolve.myr/
use "alloc.use"
use "die.use"
use "endian.use"
use "error.use"
use "fmt.use"
use "slcp.use"
use "sys.use"
use "types.use"
pkg std =
type resolveerr = union
`Badhost
`Badsrv
`Badquery
;;
type hostinfo = struct
flags : uint32
fam : sockfam
stype : socktype
proto : uint32
addr : sockaddr[:]
canon : byte[:]
next : hostinfo#
;;
const resolve : (host : byte[:] -> hostinfo#)
;;
type dnshdr = struct
id : uint16
/* {qr:1|op:4|aa:1|tc:1|rd:1|ra:1|z:3|rcode:4} */
flags : uint16
qdcnt : uint16
ancnt : uint16
nscnt : uint16
arcnt : uint16
;;
const resolve = {host : byte[:]
var hinf
hinf = zalloc()
dnsresolve(host)
-> hinf
}
const dnsresolve = {host : byte[:]
/*var hosts*/
var nsrv
if !valid(host)
-> `Failure (`Badhost)
;;
if (nsrv = dnsconnect()) < 0
-> `Failure (`Badsrv)
;;
if !dnsquery(nsrv, host)
-> `Failure (`Badquery)
;;
-> `Success true
}
const dnsconnect = {
var sa : sockaddr_in
var s
var status
s = socket(Afinet, Sockdgram, 0)
if s < 0
put("Warning: Failed to open socket: %l\n", s)
-> -1
;;
/* hardcode Google DNS for now */
sa.fam = Afinet
sa.port = hosttonet(53)
sa.addr = 0x08080808
status = connect(s, (&sa) castto(sockaddr#), sizeof(sockaddr_in))
if status < 0
put("Warning: Failed to connect to server: %l\n", status)
-> -1
;;
-> s
}
const dnsquery = {srv, host
tquery(srv, host)
rquery(srv)
put("Unimplemented query: srv=%z, host=%s\n", srv, host)
-> false
}
const Qr : uint16 = 1 << 0
const Aa : uint16 = 1 << 5
const Tc : uint16 = 1 << 6
const Rd : uint16 = 1 << 7
const Ra : uint16 = 1 << 8
var nextid = 42
const tquery = {srv, host
var pkt : byte[512] /* big enough */
var off : size
/* header */
off = 0
off += pack16(pkt[:], off, nextid++) /* id */
off += pack16(pkt[:], off, Ra) /* flags */
off += pack16(pkt[:], off, 1) /* qdcount */
off += pack16(pkt[:], off, 0) /* ancount */
off += pack16(pkt[:], off, 0) /* nscount */
off += pack16(pkt[:], off, 0) /* arcount */
/* query */
off += packname(pkt[:], off, host) /* host */
off += pack16(pkt[:], off, 0x1) /* qtype: a record */
off += pack16(pkt[:], off, 0x1) /* qclass: inet4 */
write(1, pkt[:off])
write(srv, pkt[:off])
}
const rquery = {srv
var pktbuf : byte[1024]
var pkt
var off
var nans
var nquery
var n
var v
var i
put("Waiting for response...\n")
n = read(srv, pktbuf[:])
if n < 0
put("Warning: Failed to read from %z: %i\n", srv, n)
;;
pkt = pktbuf[:n]
put("packet size = %z\n", n)
(v, off) = unpack16(pkt, 0)
put("hdr.id = %w\n", v)
(v, off) = unpack16(pkt, off)
put("hdr.rawflag = %i\n", (((v castto(uint32)) & 0xf000) >> 12))
put("hdr.flag = [Qr = %t, Aa = %t, Tc = %t, Rd = %t, Ra = %t]\n", (v&Qr) == 0, (v&Aa) == 0, (v&Tc) == 0, (v&Rd)==0, (v&Ra)==0)
put("hdr.rcode = %w\n", (v >> 11) & 0xf)
(nquery, off) = unpack16(pkt, off)
put("hdr.qdcount = %w\n", nquery)
(nans, off) = unpack16(pkt, off)
put("hdr.ancount = %w\n", nans)
(v, off) = unpack16(pkt, off)
put("hdr.nscount = %w\n", v)
(v, off) = unpack16(pkt, off)
put("hdr.arcount = %w\n", v)
put("Queries:\n")
for i = 0; i < nquery; i++
put("i: %w\n", i)
off = dumpquery(pkt, off)
;;
put("Answers:")
for i = 0; i < nans; i++
put("i: %w\n", i)
off = dumpans(pkt, off)
;;
}
const dumpquery = {pkt, off
var v
put("\tname = ");
off = printname(pkt, off)
(v, off) = unpack16(pkt, off)
put("\tbody.type = %w\n", v)
(v, off) = unpack16(pkt, off)
put("\tbody.class = %w\n", v)
-> off
}
const dumpans = {pkt, off
var v
put("\tname = ");
off = printname(pkt, off)
(v, off) = unpack16(pkt, off)
put("\tbody.type = %w\n", v)
(v, off) = unpack16(pkt, off)
put("\tbody.class = %w\n", v)
(v, off) = unpack16(pkt, off)
put("\tbody.ttl_lo = %w\n", v)
(v, off) = unpack16(pkt, off)
put("\tbody.ttl_hi = %w\n", v)
(v, off) = unpack16(pkt, off)
put("\tbody.rdlength = %w\n", v)
(v, off) = unpack16(pkt, off)
put("\tbody.rdata_lo = %w\n", v)
(v, off) = unpack16(pkt, off)
put("\tbody.rdata_hi = %w\n", v)
-> off
}
const printname = {pkt, off
var sz
for sz = pkt[off] castto(size); sz != 0; sz = pkt[off] castto(size)
if sz & 0xC0 == 0xC0
put("PTR: ")
printname(pkt, ((sz & ~0xC0) << 8) | (pkt[off + 1] castto(size)))
-> off + 2
else
put("%s.", pkt[off+1:off+sz+1])
off += sz + 1
;;
;;
-> off + 1
}
const pack16 = {buf, off, v
buf[off] = (v & 0xff00) >> 8 castto(byte)
buf[off+1] = (v & 0x00ff) castto(byte)
-> sizeof(uint16) /* we always write one uint16 */
}
const unpack16 = {buf, off
var v
v = (buf[off] castto(uint16)) << 8
v |= (buf[off + 1] castto(uint16))
-> (v, off+sizeof(uint16))
}
const packname = {buf, off : size, host
var i
var start
var seglen
var lastseg
start = off
seglen = 0
lastseg = 0
for i = 0; i < host.len; i++
seglen++
if host[i] == ('.' castto(byte))
off += addseg(buf, off, host[lastseg:lastseg+seglen-1])
lastseg = seglen
seglen = 0
;;
;;
if host[host.len - 1] != ('.' castto(byte))
off += addseg(buf, off, host[lastseg:lastseg + seglen])
;;
off += addseg(buf, off, "") /* null terminating segment */
-> off - start
}
const addseg = {buf, off, str
buf[off] = str.len castto(byte)
slcp(buf[off + 1 : off + str.len + 1], str)
-> str.len + 1
}
const valid = {host : byte[:]
var i
var seglen
/* maximum length: 255 chars */
if host.len > 255
-> false
;;
seglen = 0
for i = 0; i < host.len; i++
if host[i] == ('.' castto(byte))
seglen = 0
;;
if seglen > 63
-> false
;;
if host[i] & 0x80
-> false
;;
;;
-> true
}