commit ce70a9be245b4e2c41d7d40d9d7de123ee2a0aab (tree)
parent 6c1728206288264c4d2508a190d392ade68d115c
Author: Andrew Kelley <andrew@ziglang.org>
Date: Mon, 4 Nov 2019 15:22:14 -0500
Merge pull request #3589 from Vexu/ipv6-improvements
Ipv6 improvements
Diffstat:
2 files changed, 87 insertions(+), 60 deletions(-)
diff --git a/lib/std/net.zig b/lib/std/net.zig
@@ -32,6 +32,7 @@ pub const IpAddress = extern union {
error.InvalidEnd,
error.InvalidCharacter,
error.Incomplete,
+ error.InvalidIpv4Mapping,
=> {},
}
@@ -50,19 +51,22 @@ pub const IpAddress = extern union {
pub fn parseIp6(buf: []const u8, port: u16) !IpAddress {
var result = IpAddress{
.in6 = os.sockaddr_in6{
- .scope_id = undefined,
+ .scope_id = 0,
.port = mem.nativeToBig(u16, port),
.flowinfo = 0,
.addr = undefined,
},
};
- const ip_slice = result.in6.addr[0..];
+ var ip_slice = result.in6.addr[0..];
+
+ var tail: [16]u8 = undefined;
var x: u16 = 0;
var saw_any_digits = false;
var index: u8 = 0;
var scope_id = false;
- for (buf) |c| {
+ var abbrv = false;
+ for (buf) |c, i| {
if (scope_id) {
if (c >= '0' and c <= '9') {
const digit = c - '0';
@@ -77,7 +81,12 @@ pub const IpAddress = extern union {
}
} else if (c == ':') {
if (!saw_any_digits) {
- return error.InvalidCharacter;
+ if (abbrv) return error.InvalidCharacter; // ':::'
+ if (i != 0) abbrv = true;
+ mem.set(u8, ip_slice[index..], 0);
+ ip_slice = tail[0..];
+ index = 0;
+ continue;
}
if (index == 14) {
return error.InvalidEnd;
@@ -93,14 +102,26 @@ pub const IpAddress = extern union {
if (!saw_any_digits) {
return error.InvalidCharacter;
}
- if (index == 14) {
- ip_slice[index] = @truncate(u8, x >> 8);
- index += 1;
- ip_slice[index] = @truncate(u8, x);
- index += 1;
- }
scope_id = true;
saw_any_digits = false;
+ } else if (c == '.') {
+ if (!abbrv or ip_slice[0] != 0xff or ip_slice[1] != 0xff) {
+ // must start with '::ffff:'
+ return error.InvalidIpv4Mapping;
+ }
+ const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;
+ const addr = (parseIp4(buf[start_index..], 0) catch {
+ return error.InvalidIpv4Mapping;
+ }).in.addr;
+ ip_slice = result.in6.addr[0..];
+ ip_slice[10] = 0xff;
+ ip_slice[11] = 0xff;
+
+ ip_slice[12] = @truncate(u8, addr >> 24 & 0xff);
+ ip_slice[13] = @truncate(u8, addr >> 16 & 0xff);
+ ip_slice[14] = @truncate(u8, addr >> 8 & 0xff);
+ ip_slice[15] = @truncate(u8, addr & 0xff);
+ return result;
} else {
const digit = try std.fmt.charToDigit(c, 16);
if (@mulWithOverflow(u16, x, 16, &x)) {
@@ -113,21 +134,22 @@ pub const IpAddress = extern union {
}
}
- if (!saw_any_digits) {
+ if (!saw_any_digits and !abbrv) {
return error.Incomplete;
}
- if (scope_id) {
- return result;
- }
-
if (index == 14) {
ip_slice[14] = @truncate(u8, x >> 8);
ip_slice[15] = @truncate(u8, x);
return result;
+ } else {
+ ip_slice[index] = @truncate(u8, x >> 8);
+ index += 1;
+ ip_slice[index] = @truncate(u8, x);
+ index += 1;
+ mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);
+ return result;
}
-
- return error.Incomplete;
}
pub fn parseIp4(buf: []const u8, port: u16) !IpAddress {
@@ -246,10 +268,6 @@ pub const IpAddress = extern union {
);
},
os.AF_INET6 => {
- const ZeroRun = struct {
- index: usize,
- count: usize,
- };
const port = mem.bigToNative(u16, self.in6.port);
const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr);
const native_endian_parts = switch (builtin.endian) {
@@ -262,44 +280,21 @@ pub const IpAddress = extern union {
break :blk buf;
},
};
-
- var longest_zero_run: ?ZeroRun = null;
- var this_zero_run: ?ZeroRun = null;
- for (native_endian_parts) |part, i| {
- if (part == 0) {
- if (this_zero_run) |*zr| {
- zr.count += 1;
- } else {
- this_zero_run = ZeroRun{
- .index = i,
- .count = 1,
- };
- }
- } else if (this_zero_run) |zr| {
- if (longest_zero_run) |lzr| {
- if (zr.count > lzr.count and zr.count > 1) {
- longest_zero_run = zr;
- }
- } else {
- longest_zero_run = zr;
- }
- }
- }
try output(context, "[");
var i: usize = 0;
- while (i < native_endian_parts.len) {
- if (i != 0) try output(context, ":");
-
- if (longest_zero_run) |lzr| {
- if (lzr.index == i) {
- i += lzr.count;
- continue;
+ var abbrv = false;
+ while (i < native_endian_parts.len) : (i += 1) {
+ if (native_endian_parts[i] == 0) {
+ if (!abbrv) {
+ try output(context, if (i == 0) "::" else ":");
+ abbrv = true;
}
+ continue;
+ }
+ try std.fmt.format(context, Errors, output, "{x}", native_endian_parts[i]);
+ if (i != native_endian_parts.len - 1) {
+ try output(context, ":");
}
-
- const part = native_endian_parts[i];
- try std.fmt.format(context, Errors, output, "{x}", part);
- i += 1;
}
try std.fmt.format(context, Errors, output, "]:{}", port);
},
@@ -807,6 +802,7 @@ fn linuxLookupNameFromHosts(
error.InvalidCharacter,
error.Incomplete,
error.InvalidIPAddressFormat,
+ error.InvalidIpv4Mapping,
=> continue,
};
try addrs.append(LookupAddr{ .addr = addr });
diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig
@@ -4,10 +4,41 @@ const mem = std.mem;
const testing = std.testing;
test "parse and render IPv6 addresses" {
- const addr = try net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB", 80);
- var buf: [100]u8 = undefined;
- const printed = try std.fmt.bufPrint(&buf, "{}", addr);
- std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));
+ var buffer: [100]u8 = undefined;
+ const ips = [_][]const u8{
+ "FF01:0:0:0:0:0:0:FB",
+ "FF01::Fb",
+ "::1",
+ "::",
+ "2001:db8::",
+ "::1234:5678",
+ "2001:db8::1234:5678",
+ "FF01::FB%1234",
+ "::ffff:123.123.123.123",
+ };
+ const printed = [_][]const u8{
+ "ff01::fb",
+ "ff01::fb",
+ "::1",
+ "::",
+ "2001:db8::",
+ "::1234:5678",
+ "2001:db8::1234:5678",
+ "ff01::fb",
+ "::ffff:7b7b:7b7b",
+ };
+ for (ips) |ip, i| {
+ var addr = net.IpAddress.parseIp6(ip, 0) catch unreachable;
+ var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
+ std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3]));
+ }
+
+ testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6(":::", 0));
+ testing.expectError(error.Overflow, net.IpAddress.parseIp6("FF001::FB", 0));
+ testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6("FF01::Fb:zig", 0));
+ testing.expectError(error.InvalidEnd, net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB:", 0));
+ testing.expectError(error.Incomplete, net.IpAddress.parseIp6("FF01:", 0));
+ testing.expectError(error.InvalidIpv4Mapping, net.IpAddress.parseIp6("::123.123.123.123", 0));
}
test "parse and render IPv4 addresses" {
@@ -19,7 +50,7 @@ test "parse and render IPv4 addresses" {
"123.255.0.91",
"127.0.0.1",
}) |ip| {
- var addr = net.IpAddress.parseIp4(ip, 0);
+ var addr = net.IpAddress.parseIp4(ip, 0) catch unreachable;
var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
}