Landin library reference discuss source

compiler/tests/fixtures/runtime/network-addresses/main.ldn

1--  Internet addresses read from their text and written back: IPv4's four
2--  decimal parts, IPv6's groups as RFC 5952 writes them, shortened and in
3--  lower case, an IPv4-mapped address in dotted decimal, and a port after
4--  either, an IPv6 address then in brackets.  Text that is no address is
5--  `invalid_address`, a zone among it.  It runs on every target, firmware
6--  included, since an address is plain data.
7import core/cmp
8import core/net
9import core/text
10
11--  Text read without a port and what it is written back as, or "" when it
12--  is no address.
13case: type = struct
14    given: []u8
15    written: []u8
16end case
17
18ips: [30]case =
19    [(given: "127.0.0.1", written: "127.0.0.1"),
20     (given: "0.0.0.0", written: "0.0.0.0"),
21     (given: "255.255.255.255", written: "255.255.255.255"),
22     (given: "192.0.2.10", written: "192.0.2.10"),
23     (given: "256.1.1.1", written: ""),
24     (given: "01.2.3.4", written: ""),
25     (given: "1.2.3", written: ""),
26     (given: "1.2.3.4.5", written: ""),
27     (given: "1..2.3", written: ""),
28     (given: " 1.2.3.4", written: ""),
29     (given: "::", written: "::"),
30     (given: "::1", written: "::1"),
31     (given: "1::", written: "1::"),
32     (given: "2001:DB8:0:0:0:0:0:1", written: "2001:db8::1"),
33     (given: "2001:0db8:0000:0000:0000:ff00:0042:8329",
34      written: "2001:db8::ff00:42:8329"),
35     (given: "2001:db8::1:0:0:1", written: "2001:db8::1:0:0:1"),
36     (given: "2001:0:0:1:0:0:0:1", written: "2001:0:0:1::1"),
37     (given: "1:2:3:4:5:6:7::", written: "1:2:3:4:5:6:7:0"),
38     (given: "::ffff:192.0.2.1", written: "::ffff:192.0.2.1"),
39     (given: "::ffff:c000:201", written: "::ffff:192.0.2.1"),
40     (given: "64:ff9b::192.0.2.33", written: "64:ff9b::c000:221"),
41     (given: "1:2:3:4:5:6:7:8", written: "1:2:3:4:5:6:7:8"),
42     (given: "fe80::1%eth0", written: ""),
43     (given: "1::2::3", written: ""),
44     (given: ":1", written: ""),
45     (given: "1:", written: ""),
46     (given: "12345::", written: ""),
47     (given: "1:2:3:4:5:6:7:8:9", written: ""),
48     (given: "::g", written: ""),
49     (given: "", written: "")]
50
51--  Text read with a port and what it is written back as, or "".
52addresses: [11]case =
53    [(given: "127.0.0.1:80", written: "127.0.0.1:80"),
54     (given: "0.0.0.0:0", written: "0.0.0.0:0"),
55     (given: "[::1]:8080", written: "[::1]:8080"),
56     (given: "[2001:DB8::1]:65535", written: "[2001:db8::1]:65535"),
57     (given: "[::ffff:10.0.0.1]:443", written: "[::ffff:10.0.0.1]:443"),
58     (given: "[::1]", written: ""),
59     (given: "1.2.3.4:65536", written: ""),
60     (given: "1.2.3.4:", written: ""),
61     (given: "::1:80", written: ""),
62     (given: "[fe80::1%eth0]:80", written: ""),
63     (given: "1.2.3.4:x", written: "")]
64
65--  Whether reading C's text, with or without a port, writes it back as it
66--  says, or refuses it when it says "".
67agrees: (item: case, port: bool) -> (yes: bool) ! text.no_space =
68    mut room: [64]u8 = zeroed
69    mut line := text.over(room[0..<64])
70    wanted: []u8 = item.written
71    yes = lenof wanted == 0
72    at: net.address = read(item.given, port) else (problem)
73        _ = problem
74        return
75    end
76    yes = false
77    return when lenof wanted == 0
78    if port then
79        try net.append_address(line, at)
80    else
81        try net.append_ip(line, at)
82    end if
83    yes = cmp.compare(text.used(line), wanted) == cmp.equal
84    return when not yes
85    --  What is written reads back as the same address.
86    again: net.address = read(text.used(line), port) else (problem)
87        _ = problem
88        yes = false
89        return
90    end
91    yes = net.same_address(at, again)
92end agrees
93
94read: (source: []u8, port: bool) -> (at: net.address) ! net.invalid_address =
95    if port then
96        at = try net.parse_address(source)
97    else
98        at = try net.parse_ip(source)
99    end if
100end read
101
102--  The constructors, the comparisons and the builder's rule that a piece
103--  is written whole or not at all.
104made: () -> (yes: bool) ! text.no_space | text.invalid_format =
105    groups: [8]u16 = [0x2001, 0xdb8, 0, 0, 0, 0, 0, 1]
106    v6: net.address = net.ipv6_address(groups, 443)
107    v4: net.address = net.ipv4_address(192, 0, 2, 1, 80)
108    mut room: [64]u8 = zeroed
109    mut line := text.over(room[0..<64])
110    try text.format(line, "{} {}", [any(addr v4), any(addr v6)])
111    yes = cmp.compare(text.used(line), "192.0.2.1:80 [2001:db8::1]:443")
112          == cmp.equal
113    yes = yes and net.same_address(net.loopback(net.ipv4, 7),
114                                   net.ipv4_address(127, 0, 0, 1, 7))
115    yes = yes and not net.same_address(net.loopback(net.ipv4, 7),
116                                       net.loopback(net.ipv4, 8))
117    yes = yes and not net.same_address(net.unspecified(net.ipv4, 0),
118                                       net.unspecified(net.ipv6, 0))
119    yes = yes and net.is_unspecified(net.unspecified(net.ipv6, 9))
120          and not net.is_unspecified(net.loopback(net.ipv6, 9))
121    moved: net.address = net.with_port(v4, 81)
122    yes = yes and moved.port == 81 and moved.octets[3] == 1
123    --  Twelve bytes have room for `[::1]` but not for its port.
124    mut small: [12]u8 = zeroed
125    mut short := text.over(small[0..<12])
126    try text.append(short, "at ")
127    mut full: bool = false
128    net.append_address(short, v6) else (problem)
129        full = problem == text.no_space
130    end
131    yes = yes and full and text.length(short) == 3
132end made
133
134public main: () -> (code: i32) =
135    code = 1
136    for index in usize(0)..<lenof ips do
137        good: bool = agrees(ips[index], false) else (problem)
138            _ = problem
139            return
140        end
141        if not good then
142            code = 10 + i32(index)
143            return
144        end if
145    end for
146    code = 2
147    for index in usize(0)..<lenof addresses do
148        good: bool = agrees(addresses[index], true) else (problem)
149            _ = problem
150            return
151        end
152        if not good then
153            code = 50 + i32(index)
154            return
155        end if
156    end for
157    code = 3
158    built: bool = made() else (problem)
159        _ = problem
160        return
161    end
162    return when not built
163    code = 42
164end main