improve ipv6 parsing and formatting

This commit is contained in:
Vexu 2019-11-04 18:59:14 +02:00
parent 788848e123
commit 32ac1b5927
No known key found for this signature in database
GPG Key ID: 5AEABFCAFF5CD8D6
2 changed files with 64 additions and 60 deletions

View File

@ -50,19 +50,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 +80,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,12 +101,6 @@ 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 {
@ -113,21 +115,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 +249,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 +261,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;
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);
},

View File

@ -4,10 +4,38 @@ 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",
};
const printed = [_][]const u8{
"ff01::fb",
"ff01::fb",
"::1",
"::",
"2001:db8::",
"::1234:5678",
"2001:db8::1234:5678",
"ff01::fb"
};
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));
}
test "parse and render IPv4 addresses" {
@ -19,7 +47,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]));
}