shithub: mc

Download patch

ref: 980f6c7548c5917cef835c84c0aeb96773175785
parent: b7267a73ef2d703263173152bb219955aaffa76e
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Jun 21 18:42:47 EDT 2015

Add ipv6 parsing support.

    And a unit test.

--- a/libstd/ipparse.myr
+++ b/libstd/ipparse.myr
@@ -4,6 +4,10 @@
 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"
@@ -40,16 +44,16 @@
 	var j : size
 	*/
 
-	(a, ip, ok) = octet(ip, 10, true)
-	(ip, ok) = dot(ip, ok)
-	(b, ip, ok) = octet(ip, 10, ok)
-	(ip, ok) = dot(ip, ok)
-	(c, ip, ok) = octet(ip, 10, ok)
-	(ip, ok) = dot(ip, ok)
-	(d, ip, ok) = octet(ip, 10, ok)
+	(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])
+		-> `Some (`Ipv4 [a, b, c, d])
 	else
 		-> `None
 	;;
@@ -56,33 +60,85 @@
 }
 
 const ip6parse = {ip
-	-> `None
+	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' */
+		(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
+	;;
 }
 
-const dot = {ip, ok
-	if ip.len > 0 && ip[0] == '.' castto(byte)
+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)
 	;;
 }
-const octet = {ip, base, ok
+
+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] == '.' castto(byte)
+		if ip[len] == sep castto(byte)
 			break
 		;;
 	;;
 	match intparsebase(ip[:len], base)
 	| `std.Some v:
-		if v < 0 || v > 255
+		if v < lo || v > hi
 			-> (0, "", false)
 		;;
-		-> (v castto(byte), ip[len:], true)
+		-> (v castto(@a::(numeric,integral)), ip[len:], true)
 	| `std.None:
 		-> (0, "", false)
 	;;
--- /dev/null
+++ b/libstd/test/ipparse.myr
@@ -1,0 +1,64 @@
+use std
+
+const main = {
+	/* valid ipv4 address */
+	eq("1.2.3.4", `std.Some `std.Ipv4 [1,2,3,4])
+
+	/* invalid ipv4 address */
+	eq("1.3.4", `std.None)	/* too short */
+	eq("1.2.3.4.5", `std.None)	/* too long */
+	eq("1.3.4.256", `std.None)	/* out of bounds 1 */
+	eq("260.2.3.4", `std.None)	/* out of bounds 2 */
+
+	/* valid ipv6 addresses */
+	eq("2a03:2880:2110:df07:face:b00c:0:1", \
+		`std.Some `std.Ipv6 [0x2a, 0x03, 0x28, 0x80, 0x21, 0x10,
+			0xdf, 0xfa, 0xce, 0xb0, 0x0c, 0x00, 0x00, 0x01, 0x01])
+	eq("abcd::dcba", \
+		`std.Some `std.Ipv6 [0xab, 0xcd, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+	eq("::abcd:dcba", \
+		`std.Some `std.Ipv6 [0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xcd, 0xdc, 0xba])
+	eq("::", `std.Some `std.Ipv6 [0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+
+	/* invalid ipv4 addresses */
+	eq("2a03:2880:2110:df07:face:b00c:0:1:abc", `std.None)	/* too long */
+	eq("2a03:2880:2110:df07:face:b00c:0", `std.None)	/* too short */
+	eq("2a03:2880:2110:df07:face:b00c:0:1:", `std.None)	/* trailing ':' */
+}
+
+const eq = {ip, expected
+	var parsed
+	var p, e
+
+	parsed = std.ipparse(ip)
+	p = ipbytes(parsed)
+	e = ipbytes(expected)
+	if !std.sleq(p, e)
+		std.fput(1, "misparsed ip {}\n", ip)
+		std.put("parsed: ")
+		for b in p
+			std.put("{x}, ", b)
+		;;
+		std.put("\nexpected: ")
+		for b in e
+			std.put("{x}, ", b)
+		;;
+		std.put("\n")
+		std.fatal("failed\n")
+	;;
+}
+
+const ipbytes = {ipopt
+	match ipopt
+	| `std.Some ip:
+		match ip
+		| `std.Ipv4 b:	-> b[:]
+		| `std.Ipv6 b:	-> b[:]
+		;;
+	| `std.None:	-> [][:]
+	;;
+}
+