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
signaturebadge-question-mark 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 {...@@ -32,6 +32,7 @@ pub const IpAddress = extern union {
32 error.InvalidEnd,32 error.InvalidEnd,
33 error.InvalidCharacter,33 error.InvalidCharacter,
34 error.Incomplete,34 error.Incomplete,
35 error.InvalidIpv4Mapping,
35 => {},36 => {},
36 }37 }
3738
...@@ -50,19 +51,22 @@ pub const IpAddress = extern union {...@@ -50,19 +51,22 @@ pub const IpAddress = extern union {
50 pub fn parseIp6(buf: []const u8, port: u16) !IpAddress {51 pub fn parseIp6(buf: []const u8, port: u16) !IpAddress {
51 var result = IpAddress{52 var result = IpAddress{
52 .in6 = os.sockaddr_in6{53 .in6 = os.sockaddr_in6{
53 .scope_id = undefined,54 .scope_id = 0,
54 .port = mem.nativeToBig(u16, port),55 .port = mem.nativeToBig(u16, port),
55 .flowinfo = 0,56 .flowinfo = 0,
56 .addr = undefined,57 .addr = undefined,
57 },58 },
58 };59 };
59 const ip_slice = result.in6.addr[0..];60 var ip_slice = result.in6.addr[0..];
61
62 var tail: [16]u8 = undefined;
6063
61 var x: u16 = 0;64 var x: u16 = 0;
62 var saw_any_digits = false;65 var saw_any_digits = false;
63 var index: u8 = 0;66 var index: u8 = 0;
64 var scope_id = false;67 var scope_id = false;
65 for (buf) |c| {68 var abbrv = false;
69 for (buf) |c, i| {
66 if (scope_id) {70 if (scope_id) {
67 if (c >= '0' and c <= '9') {71 if (c >= '0' and c <= '9') {
68 const digit = c - '0';72 const digit = c - '0';
...@@ -77,7 +81,12 @@ pub const IpAddress = extern union {...@@ -77,7 +81,12 @@ pub const IpAddress = extern union {
77 }81 }
78 } else if (c == ':') {82 } else if (c == ':') {
79 if (!saw_any_digits) {83 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;
81 }90 }
82 if (index == 14) {91 if (index == 14) {
83 return error.InvalidEnd;92 return error.InvalidEnd;
...@@ -93,14 +102,26 @@ pub const IpAddress = extern union {...@@ -93,14 +102,26 @@ pub const IpAddress = extern union {
93 if (!saw_any_digits) {102 if (!saw_any_digits) {
94 return error.InvalidCharacter;103 return error.InvalidCharacter;
95 }104 }
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 }
102 scope_id = true;105 scope_id = true;
103 saw_any_digits = false;106 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;
104 } else {125 } else {
105 const digit = try std.fmt.charToDigit(c, 16);126 const digit = try std.fmt.charToDigit(c, 16);
106 if (@mulWithOverflow(u16, x, 16, &x)) {127 if (@mulWithOverflow(u16, x, 16, &x)) {
...@@ -113,21 +134,22 @@ pub const IpAddress = extern union {...@@ -113,21 +134,22 @@ pub const IpAddress = extern union {
113 }134 }
114 }135 }
115136
116 if (!saw_any_digits) {137 if (!saw_any_digits and !abbrv) {
117 return error.Incomplete;138 return error.Incomplete;
118 }139 }
119140
120 if (scope_id) {
121 return result;
122 }
123
124 if (index == 14) {141 if (index == 14) {
125 ip_slice[14] = @truncate(u8, x >> 8);142 ip_slice[14] = @truncate(u8, x >> 8);
126 ip_slice[15] = @truncate(u8, x);143 ip_slice[15] = @truncate(u8, x);
127 return result;144 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;
128 }152 }
129
130 return error.Incomplete;
131 }153 }
132154
133 pub fn parseIp4(buf: []const u8, port: u16) !IpAddress {155 pub fn parseIp4(buf: []const u8, port: u16) !IpAddress {
...@@ -246,10 +268,6 @@ pub const IpAddress = extern union {...@@ -246,10 +268,6 @@ pub const IpAddress = extern union {
246 );268 );
247 },269 },
248 os.AF_INET6 => {270 os.AF_INET6 => {
249 const ZeroRun = struct {
250 index: usize,
251 count: usize,
252 };
253 const port = mem.bigToNative(u16, self.in6.port);271 const port = mem.bigToNative(u16, self.in6.port);
254 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr);272 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr);
255 const native_endian_parts = switch (builtin.endian) {273 const native_endian_parts = switch (builtin.endian) {
...@@ -262,44 +280,21 @@ pub const IpAddress = extern union {...@@ -262,44 +280,21 @@ pub const IpAddress = extern union {
262 break :blk buf;280 break :blk buf;
263 },281 },
264 };282 };
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 }
288 try output(context, "[");283 try output(context, "[");
289 var i: usize = 0;284 var i: usize = 0;
290 while (i < native_endian_parts.len) {285 var abbrv = false;
291 if (i != 0) try output(context, ":");286 while (i < native_endian_parts.len) : (i += 1) {
292287 if (native_endian_parts[i] == 0) {
293 if (longest_zero_run) |lzr| {288 if (!abbrv) {
294 if (lzr.index == i) {289 try output(context, if (i == 0) "::" else ":");
295 i += lzr.count;290 abbrv = true;
296 continue;
297 }291 }
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, ":");
298 }297 }
299
300 const part = native_endian_parts[i];
301 try std.fmt.format(context, Errors, output, "{x}", part);
302 i += 1;
303 }298 }
304 try std.fmt.format(context, Errors, output, "]:{}", port);299 try std.fmt.format(context, Errors, output, "]:{}", port);
305 },300 },
...@@ -807,6 +802,7 @@ fn linuxLookupNameFromHosts(...@@ -807,6 +802,7 @@ fn linuxLookupNameFromHosts(
807 error.InvalidCharacter,802 error.InvalidCharacter,
808 error.Incomplete,803 error.Incomplete,
809 error.InvalidIPAddressFormat,804 error.InvalidIPAddressFormat,
805 error.InvalidIpv4Mapping,
810 => continue,806 => continue,
811 };807 };
812 try addrs.append(LookupAddr{ .addr = addr });808 try addrs.append(LookupAddr{ .addr = addr });
lib/std/net/test.zig+36-5
...@@ -4,10 +4,41 @@ const mem = std.mem;...@@ -4,10 +4,41 @@ const mem = std.mem;
4const testing = std.testing;4const testing = std.testing;
55
6test "parse and render IPv6 addresses" {6test "parse and render IPv6 addresses" {
7 const addr = try net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB", 80);7 var buffer: [100]u8 = undefined;
8 var buf: [100]u8 = undefined;8 const ips = [_][]const u8{
9 const printed = try std.fmt.bufPrint(&buf, "{}", addr);9 "FF01:0:0:0:0:0:0:FB",
10 std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));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));
11}42}
1243
13test "parse and render IPv4 addresses" {44test "parse and render IPv4 addresses" {
...@@ -19,7 +50,7 @@ test "parse and render IPv4 addresses" {...@@ -19,7 +50,7 @@ test "parse and render IPv4 addresses" {
19 "123.255.0.91",50 "123.255.0.91",
20 "127.0.0.1",51 "127.0.0.1",
21 }) |ip| {52 }) |ip| {
22 var addr = net.IpAddress.parseIp4(ip, 0);53 var addr = net.IpAddress.parseIp4(ip, 0) catch unreachable;
23 var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;54 var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
24 std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));55 std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
25 }56 }