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