summaryrefslogtreecommitdiff
path: root/lib/std/resolve+posixy.myr
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/resolve+posixy.myr')
-rw-r--r--lib/std/resolve+posixy.myr443
1 files changed, 443 insertions, 0 deletions
diff --git a/lib/std/resolve+posixy.myr b/lib/std/resolve+posixy.myr
new file mode 100644
index 0000000..185c9d7
--- /dev/null
+++ b/lib/std/resolve+posixy.myr
@@ -0,0 +1,443 @@
+use sys
+use "alloc.use"
+use "chartype.use"
+use "die.use"
+use "endian.use"
+use "result.use"
+use "extremum.use"
+use "hashfuncs.use"
+use "htab.use"
+use "ipparse.use"
+use "option.use"
+use "slcp.use"
+use "sleq.use"
+use "slpush.use"
+use "slurp.use"
+use "strfind.use"
+use "strsplit.use"
+use "strstrip.use"
+use "types.use"
+use "utf.use"
+
+pkg std =
+ type rectype = uint16
+
+ const DnsA : rectype = 1 /* host address */
+ const DnsNS : rectype = 2 /* authoritative name server */
+ const DnsMD : rectype = 3 /* mail destination (Obsolete - use MX) */
+ const DnsMF : rectype = 4 /* mail forwarder (Obsolete - use MX) */
+ const DnsCNAME : rectype = 5 /* canonical name for an alias */
+ const DnsSOA : rectype = 6 /* marks the start of a zone of authority */
+ const DnsMB : rectype = 7 /* mailbox domain name (EXPERIMENTAL) */
+ const DnsMG : rectype = 8 /* mail group member (EXPERIMENTAL) */
+ const DnsMR : rectype = 9 /* mail rename domain name (EXPERIMENTAL) */
+ const DnsNULL : rectype = 10 /* null RR (EXPERIMENTAL) */
+ const DnsWKS : rectype = 11 /* well known service description */
+ const DnsPTR : rectype = 12 /* domain name pointer */
+ const DnsHINFO : rectype = 13 /* host information */
+ const DnsMINFO : rectype = 14 /* mailbox or mail list information */
+ const DnsMX : rectype = 15 /* mail exchange */
+ const DnsTXT : rectype = 16 /* text strings */
+ const DnsAAAA : rectype = 28 /* ipv6 host address */
+
+
+ type resolveerr = union
+ `Badhost
+ `Badsrv
+ `Badquery
+ `Badresp
+ ;;
+
+ type hostinfo = struct
+ fam : sys.sockfam
+ stype : sys.socktype
+ ttl : uint32
+ addr : netaddr
+ /*
+ flags : uint32
+ addr : sockaddr[:]
+ canon : byte[:]
+ */
+ ;;
+
+ const resolve : (host : byte[:] -> result(hostinfo[:], resolveerr))
+ const resolvemx : (host : byte[:] -> result(hostinfo[:], resolveerr))
+ const resolverec : (host : byte[:], t : rectype -> result(hostinfo[:], resolveerr))
+;;
+
+const Hostfile = "/etc/hosts"
+const Resolvfile = "/etc/resolv.conf"
+
+var hostmap : htab(byte[:], hostinfo)#
+var search : byte[:][:]
+var nameservers : netaddr[:]
+
+const __init__ = {
+ hostmap = mkht(strhash, streq)
+ loadhosts()
+ loadresolv()
+}
+
+const resolve = {host
+ -> resolverec(host, DnsA)
+}
+
+const resolvemx = {host
+ -> resolverec(host, DnsMX)
+}
+
+const resolverec = {host, t
+ match hostfind(host)
+ | `Some hinf:
+ -> `Ok slpush([][:], hinf)
+ | `None:
+ -> dnsresolve(host, DnsA)
+ ;;
+}
+
+const hostfind = {host
+ -> htget(hostmap, host)
+}
+
+const loadhosts = {
+ var h
+ var lines
+
+ match slurp(Hostfile)
+ | `Ok d: h = d
+ | `Fail m: ->
+ ;;
+
+ lines = strsplit(h, "\n")
+ for l in lines
+ /* trim comment */
+ match strfind(l, "#")
+ | `Some idx: l = l[:idx]
+ | `None: /* whole line */
+ ;;
+
+ match word(l)
+ | `Some (ip, rest):
+ match ipparse(ip)
+ | `Some addr:
+ addhosts(addr, ip, rest)
+ | `None:
+ /*
+ invalid addresses are ignored: we don't want to break stuff
+ with invalid or unsupported addresses
+ */
+
+ ;;
+ | `None:
+ ;;
+ ;;
+ slfree(lines)
+}
+
+const addhosts = {addr, as, str
+ var hinf
+ var fam
+
+ match addr
+ | `Ipv4 _: fam = sys.Afinet
+ | `Ipv6 _: fam = sys.Afinet6
+ ;;
+ while true
+ match word(str)
+ | `Some (name, rest):
+ str = rest
+ if hthas(hostmap, name)
+ continue
+ ;;
+ hinf = [
+ .fam=fam,
+ .stype = 0,
+ .ttl = 0,
+ .addr = addr
+ ]
+ htput(hostmap, name, hinf)
+ | `None:
+ ->
+ ;;
+ ;;
+}
+
+const loadresolv = {
+ var h
+ var lines
+
+ match slurp(Resolvfile)
+ | `Ok d: h = d
+ | `Fail m: ->
+ ;;
+
+ lines = strsplit(h, "\n")
+ for l in lines
+ match strfind(l, "#")
+ | `Some _idx: l = l[:_idx]
+ | `None:
+ ;;
+
+ match word(l)
+ | `Some ("nameserver", srv):
+ addns(srv)
+ | `Some (_, rest):
+ /* invalid or unrecognized commands */
+ | `None:
+ /* unrecognized lines */
+ ;;
+ ;;
+ slfree(lines)
+}
+
+const addns = {rest
+ match word(rest)
+ | `Some (name, _):
+ match ipparse(name)
+ | `Some addr:
+ nameservers = slpush(nameservers, addr)
+ | `None:
+ /* nothing */
+ ;;
+ | `None:
+ /* nothing */
+ ;;
+}
+
+const word = {s
+ var c, len
+
+ len = 0
+ s = strfstrip(s)
+ for c = decode(s[len:]); c != Badchar && !isblank(c); c = decode(s[len:])
+ len += charlen(c)
+ ;;
+ if len == 0
+ -> `None
+ else
+ -> `Some (s[:len], s[len:])
+ ;;
+}
+
+
+const dnsresolve = {host, t
+ var nsrv
+
+ if !valid(host)
+ -> `Fail (`Badhost)
+ ;;
+ for ns in nameservers
+ nsrv = dnsconnect(ns)
+ if nsrv >= 0
+ -> dnsquery(nsrv, host, t)
+ ;;
+ ;;
+ -> `Fail (`Badsrv)
+}
+
+const dnsconnect = {ns
+ match ns
+ | `Ipv4 addr: -> dnsconnectv4(addr)
+ | `Ipv6 addr: die("don't support ipv6 yet\n")
+ ;;
+}
+
+const dnsconnectv4 = {addr
+ var sa : sys.sockaddr_in
+ var s
+ var status
+
+ s = sys.socket(sys.Afinet, sys.Sockdgram, 0)
+ if s < 0
+ -> -1
+ ;;
+ sa.fam = sys.Afinet
+ sa.port = hosttonet(53)
+ sa.addr = addr
+ status = sys.connect(s, (&sa) castto(sys.sockaddr#), sizeof(sys.sockaddr_in))
+ if status < 0
+ -> -1
+ ;;
+ -> s
+}
+
+const dnsquery = {srv, host, t
+ var id
+ var r
+
+ id = tquery(srv, host, t)
+ r = rquery(srv, id)
+ -> r
+}
+
+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 : uint16 = 42
+const tquery = {srv, host, t
+ 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, t castto(uint16)) /* qtype: a record */
+ off += pack16(pkt[:], off, 0x1) /* qclass: inet4 */
+
+ sys.write(srv, pkt[:off])
+ -> nextid++
+}
+
+const rquery = {srv, id
+ var pktbuf : byte[1024]
+ var pkt
+ var n
+
+ n = sys.read(srv, pktbuf[:])
+ if n < 0
+ ;;
+ pkt = pktbuf[:n]
+ -> hosts(pkt, id)
+}
+
+const hosts = {pkt, id : uint16
+ var off
+ var v, q, a
+ var i
+ var hinf : hostinfo[:]
+
+ off = 0
+ /* parse header */
+ (v, off) = unpack16(pkt, off) /* id */
+ if v != id
+ -> `Fail (`Badresp)
+ ;;
+ (v, off) = unpack16(pkt, off) /* flags */
+ (q, off) = unpack16(pkt, off) /* qdcount */
+ (a, off) = unpack16(pkt, off) /* ancount */
+ (v, off) = unpack16(pkt, off) /* nscount */
+ (v, off) = unpack16(pkt, off) /* arcount */
+
+ /* skip past query records */
+ for i = 0; i < q; i++
+ off = skipname(pkt, off) /* name */
+ (v, off) = unpack16(pkt, off) /* type */
+ (v, off) = unpack16(pkt, off) /* class */
+ ;;
+
+ /* parse answer records */
+ hinf = slalloc(a castto(size))
+ for i = 0; i < a; i++
+ off = skipname(pkt, off) /* name */
+ (v, off) = unpack16(pkt, off) /* type */
+ (v, off) = unpack16(pkt, off) /* class */
+ (hinf[i].ttl, off) = unpack32(pkt, off) /* ttl */
+ (v, off) = unpack16(pkt, off) /* rdatalen */
+ /* the thing we're interested in: our IP address */
+ hinf[i].addr = `Ipv4 [pkt[off], pkt[off+1], pkt[off+2], pkt[off+3]]
+ off += 4;
+ ;;
+ -> `Ok hinf
+}
+
+
+const skipname = {pkt, off
+ var sz
+
+ for sz = pkt[off] castto(size); sz != 0; sz = pkt[off] castto(size)
+ /* ptr is 2 bytes */
+ if sz & 0xC0 == 0xC0
+ -> off + 2
+ else
+ 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 unpack32 = {buf, off
+ var v
+
+ v = (buf[off] castto(uint32)) << 24
+ v |= (buf[off+1] castto(uint32)) << 32
+ v |= (buf[off+2] castto(uint32)) << 8
+ v |= (buf[off+3] castto(uint32))
+ -> (v, off+sizeof(uint32))
+}
+
+const packname = {buf, off : size, host
+ var i
+ var start
+ var last
+
+ start = off
+ last = 0
+ for i = 0; i < host.len; i++
+ if host[i] == ('.' castto(byte))
+ off += addseg(buf, off, host[last:i])
+ last = i + 1
+ ;;
+ ;;
+ if host[host.len - 1] != ('.' castto(byte))
+ off += addseg(buf, off, host[last:])
+ ;;
+ 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 != 0
+ -> false
+ ;;
+ ;;
+
+ -> true
+}