authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-04 15:22:14-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-11-04 15:22:14-05:00
logce70a9be245b4e2c41d7d40d9d7de123ee2a0aab
tree6343935b9f028bb438c05a418975e2091186f7cc
parent6c1728206288264c4d2508a190d392ade68d115c
parent6e786b60d4fb3a39b717e077d034131be613d6aa
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #3589 from Vexu/ipv6-improvements

Ipv6 improvements

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

lib/std/net.zig+51-55
......@@ -32,6 +32,7 @@ pub const IpAddress = extern union {
3232 error.InvalidEnd,
3333 error.InvalidCharacter,
3434 error.Incomplete,
35 error.InvalidIpv4Mapping,
3536 => {},
3637 }
3738
......@@ -50,19 +51,22 @@ pub const IpAddress = extern union {
5051 pub fn parseIp6(buf: []const u8, port: u16) !IpAddress {
5152 var result = IpAddress{
5253 .in6 = os.sockaddr_in6{
53 .scope_id = undefined,
54 .scope_id = 0,
5455 .port = mem.nativeToBig(u16, port),
5556 .flowinfo = 0,
5657 .addr = undefined,
5758 },
5859 };
59 const ip_slice = result.in6.addr[0..];
60 var ip_slice = result.in6.addr[0..];
61
62 var tail: [16]u8 = undefined;
6063
6164 var x: u16 = 0;
6265 var saw_any_digits = false;
6366 var index: u8 = 0;
6467 var scope_id = false;
65 for (buf) |c| {
68 var abbrv = false;
69 for (buf) |c, i| {
6670 if (scope_id) {
6771 if (c >= '0' and c <= '9') {
6872 const digit = c - '0';
......@@ -77,7 +81,12 @@ pub const IpAddress = extern union {
7781 }
7882 } else if (c == ':') {
7983 if (!saw_any_digits) {
80 return error.InvalidCharacter;
84 if (abbrv) return error.InvalidCharacter; // ':::'
85 if (i != 0) abbrv = true;
86 mem.set(u8, ip_slice[index..], 0);
87 ip_slice = tail[0..];
88 index = 0;
89 continue;
8190 }
8291 if (index == 14) {
8392 return error.InvalidEnd;
......@@ -93,14 +102,26 @@ pub const IpAddress = extern union {
93102 if (!saw_any_digits) {
94103 return error.InvalidCharacter;
95104 }
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 }
102105 scope_id = true;
103106 saw_any_digits = false;
107 } else if (c == '.') {
108 if (!abbrv or ip_slice[0] != 0xff or ip_slice[1] != 0xff) {
109 // must start with '::ffff:'
110 return error.InvalidIpv4Mapping;
111 }
112 const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;
113 const addr = (parseIp4(buf[start_index..], 0) catch {
114 return error.InvalidIpv4Mapping;
115 }).in.addr;
116 ip_slice = result.in6.addr[0..];
117 ip_slice[10] = 0xff;
118 ip_slice[11] = 0xff;
119
120 ip_slice[12] = @truncate(u8, addr >> 24 & 0xff);
121 ip_slice[13] = @truncate(u8, addr >> 16 & 0xff);
122 ip_slice[14] = @truncate(u8, addr >> 8 & 0xff);
123 ip_slice[15] = @truncate(u8, addr & 0xff);
124 return result;
104125 } else {
105126 const digit = try std.fmt.charToDigit(c, 16);
106127 if (@mulWithOverflow(u16, x, 16, &x)) {
......@@ -113,21 +134,22 @@ pub const IpAddress = extern union {
113134 }
114135 }
115136
116 if (!saw_any_digits) {
137 if (!saw_any_digits and !abbrv) {
117138 return error.Incomplete;
118139 }
119140
120 if (scope_id) {
121 return result;
122 }
123
124141 if (index == 14) {
125142 ip_slice[14] = @truncate(u8, x >> 8);
126143 ip_slice[15] = @truncate(u8, x);
127144 return result;
145 } else {
146 ip_slice[index] = @truncate(u8, x >> 8);
147 index += 1;
148 ip_slice[index] = @truncate(u8, x);
149 index += 1;
150 mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);
151 return result;
128152 }
129
130 return error.Incomplete;
131153 }
132154
133155 pub fn parseIp4(buf: []const u8, port: u16) !IpAddress {
......@@ -246,10 +268,6 @@ pub const IpAddress = extern union {
246268 );
247269 },
248270 os.AF_INET6 => {
249 const ZeroRun = struct {
250 index: usize,
251 count: usize,
252 };
253271 const port = mem.bigToNative(u16, self.in6.port);
254272 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr);
255273 const native_endian_parts = switch (builtin.endian) {
......@@ -262,44 +280,21 @@ pub const IpAddress = extern union {
262280 break :blk buf;
263281 },
264282 };
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 }
288283 try output(context, "[");
289284 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;
285 var abbrv = false;
286 while (i < native_endian_parts.len) : (i += 1) {
287 if (native_endian_parts[i] == 0) {
288 if (!abbrv) {
289 try output(context, if (i == 0) "::" else ":");
290 abbrv = true;
297291 }
292 continue;
293 }
294 try std.fmt.format(context, Errors, output, "{x}", native_endian_parts[i]);
295 if (i != native_endian_parts.len - 1) {
296 try output(context, ":");
298297 }
299
300 const part = native_endian_parts[i];
301 try std.fmt.format(context, Errors, output, "{x}", part);
302 i += 1;
303298 }
304299 try std.fmt.format(context, Errors, output, "]:{}", port);
305300 },
......@@ -807,6 +802,7 @@ fn linuxLookupNameFromHosts(
807802 error.InvalidCharacter,
808803 error.Incomplete,
809804 error.InvalidIPAddressFormat,
805 error.InvalidIpv4Mapping,
810806 => continue,
811807 };
812808 try addrs.append(LookupAddr{ .addr = addr });
lib/std/net/test.zig+36-5
......@@ -4,10 +4,41 @@ 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 "::ffff:123.123.123.123",
18 };
19 const printed = [_][]const u8{
20 "ff01::fb",
21 "ff01::fb",
22 "::1",
23 "::",
24 "2001:db8::",
25 "::1234:5678",
26 "2001:db8::1234:5678",
27 "ff01::fb",
28 "::ffff:7b7b:7b7b",
29 };
30 for (ips) |ip, i| {
31 var addr = net.IpAddress.parseIp6(ip, 0) catch unreachable;
32 var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
33 std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3]));
34 }
35
36 testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6(":::", 0));
37 testing.expectError(error.Overflow, net.IpAddress.parseIp6("FF001::FB", 0));
38 testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6("FF01::Fb:zig", 0));
39 testing.expectError(error.InvalidEnd, net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB:", 0));
40 testing.expectError(error.Incomplete, net.IpAddress.parseIp6("FF01:", 0));
41 testing.expectError(error.InvalidIpv4Mapping, net.IpAddress.parseIp6("::123.123.123.123", 0));
1142}
1243
1344test "parse and render IPv4 addresses" {
......@@ -19,7 +50,7 @@ test "parse and render IPv4 addresses" {
1950 "123.255.0.91",
2051 "127.0.0.1",
2152 }) |ip| {
22 var addr = net.IpAddress.parseIp4(ip, 0);
53 var addr = net.IpAddress.parseIp4(ip, 0) catch unreachable;
2354 var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
2455 std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
2556 }