authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2019-11-04 18:59:14+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2019-11-04 19:00:58+02:00
log32ac1b5927ba9314b9fa0ec2bb21dba4b2e66c66
treede5e239a768caed4d9f1d9d525f22a9811ce88ec
parent788848e123c056b6204f7555a0118377aa4bd8e1
signaturelock-open Commit is signed but in an unrecognized format.

improve ipv6 parsing and formatting


2 files changed, 64 insertions(+), 60 deletions(-)

lib/std/net.zig+31-55
......@@ -50,19 +50,22 @@ pub const IpAddress = extern union {
5050 pub fn parseIp6(buf: []const u8, port: u16) !IpAddress {
5151 var result = IpAddress{
5252 .in6 = os.sockaddr_in6{
53 .scope_id = undefined,
53 .scope_id = 0,
5454 .port = mem.nativeToBig(u16, port),
5555 .flowinfo = 0,
5656 .addr = undefined,
5757 },
5858 };
59 const ip_slice = result.in6.addr[0..];
59 var ip_slice = result.in6.addr[0..];
60
61 var tail: [16]u8 = undefined;
6062
6163 var x: u16 = 0;
6264 var saw_any_digits = false;
6365 var index: u8 = 0;
6466 var scope_id = false;
65 for (buf) |c| {
67 var abbrv = false;
68 for (buf) |c, i| {
6669 if (scope_id) {
6770 if (c >= '0' and c <= '9') {
6871 const digit = c - '0';
......@@ -77,7 +80,12 @@ pub const IpAddress = extern union {
7780 }
7881 } else if (c == ':') {
7982 if (!saw_any_digits) {
80 return error.InvalidCharacter;
83 if (abbrv) return error.InvalidCharacter; // ':::'
84 if (i != 0) abbrv = true;
85 mem.set(u8, ip_slice[index..], 0);
86 ip_slice = tail[0..];
87 index = 0;
88 continue;
8189 }
8290 if (index == 14) {
8391 return error.InvalidEnd;
......@@ -93,12 +101,6 @@ pub const IpAddress = extern union {
93101 if (!saw_any_digits) {
94102 return error.InvalidCharacter;
95103 }
96 if (index == 14) {
97 ip_slice[index] = @truncate(u8, x >> 8);
98 index += 1;
99 ip_slice[index] = @truncate(u8, x);
100 index += 1;
101 }
102104 scope_id = true;
103105 saw_any_digits = false;
104106 } else {
......@@ -113,21 +115,22 @@ pub const IpAddress = extern union {
113115 }
114116 }
115117
116 if (!saw_any_digits) {
118 if (!saw_any_digits and !abbrv) {
117119 return error.Incomplete;
118120 }
119121
120 if (scope_id) {
121 return result;
122 }
123
124122 if (index == 14) {
125123 ip_slice[14] = @truncate(u8, x >> 8);
126124 ip_slice[15] = @truncate(u8, x);
127125 return result;
126 } else {
127 ip_slice[index] = @truncate(u8, x >> 8);
128 index += 1;
129 ip_slice[index] = @truncate(u8, x);
130 index += 1;
131 mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);
132 return result;
128133 }
129
130 return error.Incomplete;
131134 }
132135
133136 pub fn parseIp4(buf: []const u8, port: u16) !IpAddress {
......@@ -246,10 +249,6 @@ pub const IpAddress = extern union {
246249 );
247250 },
248251 os.AF_INET6 => {
249 const ZeroRun = struct {
250 index: usize,
251 count: usize,
252 };
253252 const port = mem.bigToNative(u16, self.in6.port);
254253 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr);
255254 const native_endian_parts = switch (builtin.endian) {
......@@ -262,44 +261,21 @@ pub const IpAddress = extern union {
262261 break :blk buf;
263262 },
264263 };
265
266 var longest_zero_run: ?ZeroRun = null;
267 var this_zero_run: ?ZeroRun = null;
268 for (native_endian_parts) |part, i| {
269 if (part == 0) {
270 if (this_zero_run) |*zr| {
271 zr.count += 1;
272 } else {
273 this_zero_run = ZeroRun{
274 .index = i,
275 .count = 1,
276 };
277 }
278 } else if (this_zero_run) |zr| {
279 if (longest_zero_run) |lzr| {
280 if (zr.count > lzr.count and zr.count > 1) {
281 longest_zero_run = zr;
282 }
283 } else {
284 longest_zero_run = zr;
285 }
286 }
287 }
288264 try output(context, "[");
289265 var i: usize = 0;
290 while (i < native_endian_parts.len) {
291 if (i != 0) try output(context, ":");
292
293 if (longest_zero_run) |lzr| {
294 if (lzr.index == i) {
295 i += lzr.count;
296 continue;
266 var abbrv = false;
267 while (i < native_endian_parts.len) : (i += 1) {
268 if (native_endian_parts[i] == 0) {
269 if (!abbrv) {
270 try output(context, if (i == 0) "::" else ":");
271 abbrv = true;
297272 }
273 continue;
274 }
275 try std.fmt.format(context, Errors, output, "{x}", native_endian_parts[i]);
276 if (i != native_endian_parts.len - 1) {
277 try output(context, ":");
298278 }
299
300 const part = native_endian_parts[i];
301 try std.fmt.format(context, Errors, output, "{x}", part);
302 i += 1;
303279 }
304280 try std.fmt.format(context, Errors, output, "]:{}", port);
305281 },
lib/std/net/test.zig+33-5
......@@ -4,10 +4,38 @@ const mem = std.mem;
44const testing = std.testing;
55
66test "parse and render IPv6 addresses" {
7 const addr = try net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB", 80);
8 var buf: [100]u8 = undefined;
9 const printed = try std.fmt.bufPrint(&buf, "{}", addr);
10 std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));
7 var buffer: [100]u8 = undefined;
8 const ips = [_][]const u8{
9 "FF01:0:0:0:0:0:0:FB",
10 "FF01::Fb",
11 "::1",
12 "::",
13 "2001:db8::",
14 "::1234:5678",
15 "2001:db8::1234:5678",
16 "FF01::FB%1234",
17 };
18 const printed = [_][]const u8{
19 "ff01::fb",
20 "ff01::fb",
21 "::1",
22 "::",
23 "2001:db8::",
24 "::1234:5678",
25 "2001:db8::1234:5678",
26 "ff01::fb"
27 };
28 for (ips) |ip, i| {
29 var addr = net.IpAddress.parseIp6(ip, 0) catch unreachable;
30 var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
31 std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3]));
32 }
33
34 testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6(":::", 0));
35 testing.expectError(error.Overflow, net.IpAddress.parseIp6("FF001::FB", 0));
36 testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6("FF01::Fb:zig", 0));
37 testing.expectError(error.InvalidEnd, net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB:", 0));
38 testing.expectError(error.Incomplete, net.IpAddress.parseIp6("FF01:", 0));
1139}
1240
1341test "parse and render IPv4 addresses" {
......@@ -19,7 +47,7 @@ test "parse and render IPv4 addresses" {
1947 "123.255.0.91",
2048 "127.0.0.1",
2149 }) |ip| {
22 var addr = net.IpAddress.parseIp4(ip, 0);
50 var addr = net.IpAddress.parseIp4(ip, 0) catch unreachable;
2351 var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
2452 std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
2553 }