summaryrefslogtreecommitdiff
path: root/lib/std/ipparse.myr
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/ipparse.myr')
-rw-r--r--lib/std/ipparse.myr147
1 files changed, 147 insertions, 0 deletions
diff --git a/lib/std/ipparse.myr b/lib/std/ipparse.myr
new file mode 100644
index 0000000..17bc6f0
--- /dev/null
+++ b/lib/std/ipparse.myr
@@ -0,0 +1,147 @@
+use "die.use"
+use "intparse.use"
+use "option.use"
+use "strfind.use"
+use "types.use"
+use "chartype.use"
+use "fmt.use"
+use "slcp.use"
+use "slfill.use"
+use "sleq.use"
+
+ /* FIXME: needed for decls which should be pulled in as hidden */
+use "hasprefix.use"
+use "utf.use"
+
+pkg std =
+
+ type netaddr = union
+ `Ipv4 byte[4]
+ `Ipv6 byte[16]
+ ;;
+
+ const ipparse : (ip : byte[:] -> option(netaddr))
+ const ip4parse : (ip : byte[:] -> option(netaddr))
+ const ip6parse : (ip : byte[:] -> option(netaddr))
+;;
+
+const ipparse = {ip
+ match strfind(ip, ":")
+ | `Some _: -> ip6parse(ip)
+ | `None: -> ip4parse(ip)
+ ;;
+}
+
+const ip4parse = {ip
+ var a, b, c, d
+ var ok
+ /*
+ var addr
+ var last : size
+ var x : option(int32)
+ var val : int32 /* need int32 to check for overflow */
+ var i
+ var j : size
+ */
+
+ (a, ip, ok) = num(ip, 0, 255, 10, '.', true)
+ (ip, ok) = delim(ip, '.', ok)
+ (b, ip, ok) = num(ip, 0, 255, 10, '.', ok)
+ (ip, ok) = delim(ip, '.', ok)
+ (c, ip, ok) = num(ip, 0, 255, 10, '.', ok)
+ (ip, ok) = delim(ip, '.', ok)
+ (d, ip, ok) = num(ip, 0, 255, 10, '.', ok)
+
+ if ok && ip.len == 0
+ -> `Some (`Ipv4 [a, b, c, d])
+ else
+ -> `None
+ ;;
+}
+
+const ip6parse = {ip
+ var val : byte[16]
+ var expand, split
+ var v, ok
+ var i, nseg
+
+ ok = true
+ expand = false
+ split = 0
+ if ip.len > 2 && std.sleq(ip[:2], "::")
+ expand = true
+ split = 0
+ ;;
+ nseg = 0
+ for i = 0; ip.len > 0 && ok; i++
+ /* parse 'num' segment */
+ (v, ip, ok) = num(ip, 0, 65536, 16, ':', ok)
+ nseg++
+ if ip.len == 0 || nseg == 8
+ break
+ ;;
+ (ip, ok) = delim(ip, ':', ok)
+ /* only one '::' allowed once */
+ if ip.len > 0 && ip[0] == ':'castto(byte) && !expand
+ expand = true
+ split = i
+ (ip, ok) = delim(ip, ':', ok)
+ ;;
+
+ /* pack it into the bytes */
+ val[i*2] = ((v & 0xff00) >> 8) castto(byte)
+ val[i*2 + 1] = (v & 0xff) castto(byte)
+ ;;
+
+ if ok && ip.len == 0
+ if expand
+ expandsplit(val[:], split, i)
+ elif nseg != 8
+ -> `None
+ ;;
+ -> `Some `Ipv6 val
+ else
+ -> `None
+ ;;
+}
+
+/* take "a:b::c:d" and expand it to "a:b:0:0:...:0:c:d" */
+const expandsplit = {ip, split, len
+ var width
+
+ width = 16 - len
+ std.slcp(ip[split:len], ip[split+width:len+width])
+ std.slfill(ip[len:len+width], 0)
+}
+
+const delim = {ip, sep, ok
+ if ip.len > 0 && ip[0] == sep castto(byte)
+ -> (ip[1:], ok)
+ else
+ -> ("", false)
+ ;;
+}
+
+generic num = {ip, lo, hi, base, sep, ok -> (@a::(numeric,integral), byte[:], bool)
+ var len
+
+ if !ok
+ -> (0, "", false)
+ ;;
+
+ for len = 0; len < ip.len; len++
+ if ip[len] == sep castto(byte)
+ break
+ ;;
+ ;;
+ match intparsebase(ip[:len], base)
+ | `std.Some v:
+ if v < lo || v > hi
+ -> (0, "", false)
+ ;;
+ -> (v castto(@a::(numeric,integral)), ip[len:], true)
+ | `std.None:
+ -> (0, "", false)
+ ;;
+}
+