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: -> [][:]
+ ;;
+}
+