Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 51 additions & 55 deletions lib/std/net.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub const IpAddress = extern union {
error.InvalidEnd,
error.InvalidCharacter,
error.Incomplete,
error.InvalidIpv4Mapping,
=> {},
}

Expand All @@ -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';
Expand All @@ -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;
Expand All @@ -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)) {
Expand All @@ -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 {
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
},
Expand Down Expand Up @@ -807,6 +802,7 @@ fn linuxLookupNameFromHosts(
error.InvalidCharacter,
error.Incomplete,
error.InvalidIPAddressFormat,
error.InvalidIpv4Mapping,
=> continue,
};
try addrs.append(LookupAddr{ .addr = addr });
Expand Down
41 changes: 36 additions & 5 deletions lib/std/net/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand All @@ -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]));
}
Expand Down