diff --git a/lib/std/Io/net.zig b/lib/std/Io/net.zig index 241a97467a..4c0c9405a7 100644 --- a/lib/std/Io/net.zig +++ b/lib/std/Io/net.zig @@ -70,7 +70,7 @@ pub const IpAddress = union(enum) { pub fn parseLiteral(text: []const u8) ParseLiteralError!IpAddress { if (text.len == 0) return error.InvalidAddress; if (text[0] == '[') { - const addr_end = std.mem.indexOfScalar(u8, text, ']') orelse + const addr_end = std.mem.findScalar(u8, text, ']') orelse return error.InvalidAddress; const addr_text = text[1..addr_end]; const port: u16 = p: { @@ -80,7 +80,7 @@ pub const IpAddress = union(enum) { }; return parseIp6(addr_text, port) catch error.InvalidAddress; } - if (std.mem.indexOfScalar(u8, text, ':')) |i| { + if (std.mem.findScalar(u8, text, ':')) |i| { const addr = Ip4Address.parse(text[0..i], 0) catch return error.InvalidAddress; return .{ .ip4 = .{ .bytes = addr.bytes, @@ -431,17 +431,22 @@ pub const Ip6Address = struct { pub const Parsed = union(enum) { success: Unresolved, invalid_byte: usize, - unexpected_end, + incomplete, junk_after_end: usize, interface_name_oversized: usize, + invalid_ip4_mapping: usize, + overflow: usize, }; pub fn parse(text: []const u8) Parsed { - if (text.len < 2) return .unexpected_end; - if (std.ascii.startsWithIgnoreCase(text, "::ffff:")) ip4_mapped: { - const a4 = (Ip4Address.parse(text["::ffff:".len..], 0) catch break :ip4_mapped).bytes; + if (text.len < 2) return .incomplete; + const ip4_prefix = "::ffff:"; + if (std.ascii.startsWithIgnoreCase(text, ip4_prefix)) { + const parsed = Ip4Address.parse(text[ip4_prefix.len..], 0) catch + return .{ .invalid_ip4_mapping = ip4_prefix.len }; + const b = parsed.bytes; return .{ .success = .{ - .bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a4[0], a4[1], a4[2], a4[3] }, + .bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, b[0], b[1], b[2], b[3] }, .interface_name = null, } }; } @@ -457,7 +462,9 @@ pub const Ip6Address = struct { .digit => c: switch (text[text_i]) { 'a'...'f' => |c| { const digit = c - 'a' + 10; - parts[parts_i] = parts[parts_i] * 16 + digit; + parts[parts_i] = (std.math.mul(u16, parts[parts_i], 16) catch return .{ + .overflow = text_i, + }) + digit; if (digit_i == 4) return .{ .invalid_byte = text_i }; digit_i += 1; text_i += 1; @@ -470,7 +477,9 @@ pub const Ip6Address = struct { 'A'...'F' => |c| continue :c c - 'A' + 'a', '0'...'9' => |c| { const digit = c - '0'; - parts[parts_i] = parts[parts_i] * 16 + digit; + parts[parts_i] = (std.math.mul(u16, parts[parts_i], 16) catch return .{ + .overflow = text_i, + }) + digit; if (digit_i == 4) return .{ .invalid_byte = text_i }; digit_i += 1; text_i += 1; @@ -497,7 +506,7 @@ pub const Ip6Address = struct { if (parts.len - parts_i == 0) continue :state .end; digit_i = 0; text_i += 1; - if (text.len - text_i == 0) return .unexpected_end; + if (text.len - text_i == 0) return .incomplete; continue :c text[text_i]; } }, @@ -507,6 +516,7 @@ pub const Ip6Address = struct { text_i += 1; const name = text[text_i..]; if (name.len > Interface.Name.max_len) return .{ .interface_name_oversized = text_i }; + if (name.len == 0) return .incomplete; interface_name_text = name; text_i = @intCast(text.len); continue :state .end; @@ -521,7 +531,7 @@ pub const Ip6Address = struct { @memmove(parts[parts.len - src.len ..], src); @memset(parts[s..][0..remaining], 0); } else { - if (remaining != 0) return .unexpected_end; + if (remaining != 0) return .incomplete; } // Workaround that can be removed when this proposal is diff --git a/lib/std/Io/net/test.zig b/lib/std/Io/net/test.zig index cc95237efb..9bd4618429 100644 --- a/lib/std/Io/net/test.zig +++ b/lib/std/Io/net/test.zig @@ -56,6 +56,7 @@ test "parse and render IPv6 addresses" { try testParseAndRenderIp6Address("2001:db8::1234:5678", "2001:db8::1234:5678"); try testParseAndRenderIp6Address("FF01::FB%1234", "ff01::fb%1234"); try testParseAndRenderIp6Address("::ffff:123.5.123.5", "::ffff:123.5.123.5"); + try testParseAndRenderIp6Address("ff01::fb%12345678901234", "ff01::fb%12345678901234"); } fn testParseAndRenderIp6Address(input: []const u8, expected_output: []const u8) !void { @@ -66,16 +67,19 @@ fn testParseAndRenderIp6Address(input: []const u8, expected_output: []const u8) } test "IPv6 address parse failures" { - try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6(":::", 0)); - try testing.expectError(error.Overflow, net.IpAddress.parseIp6("FF001::FB", 0)); - try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6("FF01::Fb:zig", 0)); - try testing.expectError(error.InvalidEnd, net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB:", 0)); - try testing.expectError(error.Incomplete, net.IpAddress.parseIp6("FF01:", 0)); - try testing.expectError(error.InvalidIpv4Mapping, net.IpAddress.parseIp6("::123.123.123.123", 0)); - try testing.expectError(error.Incomplete, net.IpAddress.parseIp6("1", 0)); - try testing.expectError(error.Incomplete, net.IpAddress.parseIp6("ff01::fb%", 0)); - try testing.expectError(error.Overflow, net.IpAddress.parseIp6("ff01::fb%wlp3" ++ "s0" ** @divExact(std.posix.IFNAMESIZE - 4, 2), 0)); - try testing.expectError(error.Overflow, net.IpAddress.parseIp6("ff01::fb%12345678901234", 0)); + try testing.expectError(error.ParseFailed, net.IpAddress.parseIp6(":::", 0)); + + const Unresolved = net.Ip6Address.Unresolved; + + try testing.expectEqual(Unresolved.Parsed{ .invalid_byte = 2 }, Unresolved.parse(":::")); + try testing.expectEqual(Unresolved.Parsed{ .overflow = 4 }, Unresolved.parse("FF001::FB")); + try testing.expectEqual(Unresolved.Parsed{ .invalid_byte = 9 }, Unresolved.parse("FF01::Fb:zig")); + try testing.expectEqual(Unresolved.Parsed{ .junk_after_end = 19 }, Unresolved.parse("FF01:0:0:0:0:0:0:FB:")); + try testing.expectEqual(Unresolved.Parsed.incomplete, Unresolved.parse("FF01:")); + try testing.expectEqual(Unresolved.Parsed{ .invalid_byte = 5 }, Unresolved.parse("::123.123.123.123")); + try testing.expectEqual(Unresolved.Parsed.incomplete, Unresolved.parse("1")); + try testing.expectEqual(Unresolved.Parsed.incomplete, Unresolved.parse("ff01::fb%")); + try testing.expectEqual(Unresolved.Parsed{ .interface_name_oversized = 9 }, Unresolved.parse("ff01::fb%wlp3" ++ "s0" ** @divExact(std.posix.IFNAMESIZE - 4, 2))); } test "invalid but parseable IPv6 scope ids" {