std.net: add explicit error sets for IP parsing

Inferred errors in switch statements prevented IP address parsing at comptime.
Adding explicit error sets fixes it.

Closes #18276
This commit is contained in:
Artem Kolichenkov 2023-12-16 18:15:51 +02:00 committed by GitHub
parent 779b8e2598
commit 90a19f7411
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 7 deletions

View file

@ -14,6 +14,19 @@ pub const has_unix_sockets = @hasDecl(os.sockaddr, "un") and
(builtin.target.os.tag != .windows or
builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false);
pub const IPParseError = error{
Overflow,
InvalidEnd,
InvalidCharacter,
Incomplete,
};
pub const IPv4ParseError = IPParseError || error{NonCanonical};
pub const IPv6ParseError = IPParseError || error{InvalidIpv4Mapping};
pub const IPv6InterfaceError = os.SocketError || os.IoCtl_SIOCGIFINDEX_Error || error{NameTooLong};
pub const IPv6ResolveError = IPv6ParseError || IPv6InterfaceError;
pub const Address = extern union {
any: os.sockaddr,
in: Ip4Address,
@ -77,15 +90,15 @@ pub const Address = extern union {
}
}
pub fn parseIp6(buf: []const u8, port: u16) !Address {
pub fn parseIp6(buf: []const u8, port: u16) IPv6ParseError!Address {
return Address{ .in6 = try Ip6Address.parse(buf, port) };
}
pub fn resolveIp6(buf: []const u8, port: u16) !Address {
pub fn resolveIp6(buf: []const u8, port: u16) IPv6ResolveError!Address {
return Address{ .in6 = try Ip6Address.resolve(buf, port) };
}
pub fn parseIp4(buf: []const u8, port: u16) !Address {
pub fn parseIp4(buf: []const u8, port: u16) IPv4ParseError!Address {
return Address{ .in = try Ip4Address.parse(buf, port) };
}
@ -198,7 +211,7 @@ pub const Address = extern union {
pub const Ip4Address = extern struct {
sa: os.sockaddr.in,
pub fn parse(buf: []const u8, port: u16) !Ip4Address {
pub fn parse(buf: []const u8, port: u16) IPv4ParseError!Ip4Address {
var result = Ip4Address{
.sa = .{
.port = mem.nativeToBig(u16, port),
@ -307,7 +320,7 @@ pub const Ip6Address = extern struct {
/// Parse a given IPv6 address string into an Address.
/// Assumes the Scope ID of the address is fully numeric.
/// For non-numeric addresses, see `resolveIp6`.
pub fn parse(buf: []const u8, port: u16) !Ip6Address {
pub fn parse(buf: []const u8, port: u16) IPv6ParseError!Ip6Address {
var result = Ip6Address{
.sa = os.sockaddr.in6{
.scope_id = 0,
@ -424,7 +437,7 @@ pub const Ip6Address = extern struct {
}
}
pub fn resolve(buf: []const u8, port: u16) !Ip6Address {
pub fn resolve(buf: []const u8, port: u16) IPv6ResolveError!Ip6Address {
// TODO: Unify the implementations of resolveIp6 and parseIp6.
var result = Ip6Address{
.sa = os.sockaddr.in6{
@ -659,7 +672,7 @@ pub fn connectUnixSocket(path: []const u8) !Stream {
};
}
fn if_nametoindex(name: []const u8) !u32 {
fn if_nametoindex(name: []const u8) IPv6InterfaceError!u32 {
if (builtin.target.os.tag == .linux) {
var ifr: os.ifreq = undefined;
const sockfd = try os.socket(os.AF.UNIX, os.SOCK.DGRAM | os.SOCK.CLOEXEC, 0);

View file

@ -4,6 +4,28 @@ const net = std.net;
const mem = std.mem;
const testing = std.testing;
test "parse and render IP addresses at comptime" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
comptime {
var ipAddrBuffer: [16]u8 = undefined;
// Parses IPv6 at comptime
const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable;
var ipv6 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv6addr}) catch unreachable;
try std.testing.expect(std.mem.eql(u8, "::1", ipv6[1 .. ipv6.len - 3]));
// Parses IPv4 at comptime
const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable;
var ipv4 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv4addr}) catch unreachable;
try std.testing.expect(std.mem.eql(u8, "127.0.0.1", ipv4[0 .. ipv4.len - 2]));
// Returns error for invalid IP addresses at comptime
try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("::123.123.123.123", 0));
try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("127.01.0.1", 0));
try testing.expectError(error.InvalidIPAddressFormat, net.Address.resolveIp("::123.123.123.123", 0));
try testing.expectError(error.InvalidIPAddressFormat, net.Address.resolveIp("127.01.0.1", 0));
}
}
test "parse and render IPv6 addresses" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;