authorgravatar for artem@kolichenkov.comArtem Kolichenkov <artem@kolichenkov.com> 2023-12-16 18:15:51+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-12-16 16:15:51+00:00
log90a19f74116eb09e4711d845d08c011cb62b8cbf
tree84c3c9cc793af76dacf69f7adbb53b53cd3f221b
parent779b8e2598521de45934c085ebece852bf5039de
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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

2 files changed, 42 insertions(+), 7 deletions(-)

lib/std/net.zig+20-7
...@@ -14,6 +14,19 @@ pub const has_unix_sockets = @hasDecl(os.sockaddr, "un") and...@@ -14,6 +14,19 @@ pub const has_unix_sockets = @hasDecl(os.sockaddr, "un") and
14 (builtin.target.os.tag != .windows or14 (builtin.target.os.tag != .windows or
15 builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false);15 builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false);
1616
17pub const IPParseError = error{
18 Overflow,
19 InvalidEnd,
20 InvalidCharacter,
21 Incomplete,
22};
23
24pub const IPv4ParseError = IPParseError || error{NonCanonical};
25
26pub const IPv6ParseError = IPParseError || error{InvalidIpv4Mapping};
27pub const IPv6InterfaceError = os.SocketError || os.IoCtl_SIOCGIFINDEX_Error || error{NameTooLong};
28pub const IPv6ResolveError = IPv6ParseError || IPv6InterfaceError;
29
17pub const Address = extern union {30pub const Address = extern union {
18 any: os.sockaddr,31 any: os.sockaddr,
19 in: Ip4Address,32 in: Ip4Address,
...@@ -77,15 +90,15 @@ pub const Address = extern union {...@@ -77,15 +90,15 @@ pub const Address = extern union {
77 }90 }
78 }91 }
7992
80 pub fn parseIp6(buf: []const u8, port: u16) !Address {93 pub fn parseIp6(buf: []const u8, port: u16) IPv6ParseError!Address {
81 return Address{ .in6 = try Ip6Address.parse(buf, port) };94 return Address{ .in6 = try Ip6Address.parse(buf, port) };
82 }95 }
8396
84 pub fn resolveIp6(buf: []const u8, port: u16) !Address {97 pub fn resolveIp6(buf: []const u8, port: u16) IPv6ResolveError!Address {
85 return Address{ .in6 = try Ip6Address.resolve(buf, port) };98 return Address{ .in6 = try Ip6Address.resolve(buf, port) };
86 }99 }
87100
88 pub fn parseIp4(buf: []const u8, port: u16) !Address {101 pub fn parseIp4(buf: []const u8, port: u16) IPv4ParseError!Address {
89 return Address{ .in = try Ip4Address.parse(buf, port) };102 return Address{ .in = try Ip4Address.parse(buf, port) };
90 }103 }
91104
...@@ -198,7 +211,7 @@ pub const Address = extern union {...@@ -198,7 +211,7 @@ pub const Address = extern union {
198pub const Ip4Address = extern struct {211pub const Ip4Address = extern struct {
199 sa: os.sockaddr.in,212 sa: os.sockaddr.in,
200213
201 pub fn parse(buf: []const u8, port: u16) !Ip4Address {214 pub fn parse(buf: []const u8, port: u16) IPv4ParseError!Ip4Address {
202 var result = Ip4Address{215 var result = Ip4Address{
203 .sa = .{216 .sa = .{
204 .port = mem.nativeToBig(u16, port),217 .port = mem.nativeToBig(u16, port),
...@@ -307,7 +320,7 @@ pub const Ip6Address = extern struct {...@@ -307,7 +320,7 @@ pub const Ip6Address = extern struct {
307 /// Parse a given IPv6 address string into an Address.320 /// Parse a given IPv6 address string into an Address.
308 /// Assumes the Scope ID of the address is fully numeric.321 /// Assumes the Scope ID of the address is fully numeric.
309 /// For non-numeric addresses, see `resolveIp6`.322 /// For non-numeric addresses, see `resolveIp6`.
310 pub fn parse(buf: []const u8, port: u16) !Ip6Address {323 pub fn parse(buf: []const u8, port: u16) IPv6ParseError!Ip6Address {
311 var result = Ip6Address{324 var result = Ip6Address{
312 .sa = os.sockaddr.in6{325 .sa = os.sockaddr.in6{
313 .scope_id = 0,326 .scope_id = 0,
...@@ -424,7 +437,7 @@ pub const Ip6Address = extern struct {...@@ -424,7 +437,7 @@ pub const Ip6Address = extern struct {
424 }437 }
425 }438 }
426439
427 pub fn resolve(buf: []const u8, port: u16) !Ip6Address {440 pub fn resolve(buf: []const u8, port: u16) IPv6ResolveError!Ip6Address {
428 // TODO: Unify the implementations of resolveIp6 and parseIp6.441 // TODO: Unify the implementations of resolveIp6 and parseIp6.
429 var result = Ip6Address{442 var result = Ip6Address{
430 .sa = os.sockaddr.in6{443 .sa = os.sockaddr.in6{
...@@ -659,7 +672,7 @@ pub fn connectUnixSocket(path: []const u8) !Stream {...@@ -659,7 +672,7 @@ pub fn connectUnixSocket(path: []const u8) !Stream {
659 };672 };
660}673}
661674
662fn if_nametoindex(name: []const u8) !u32 {675fn if_nametoindex(name: []const u8) IPv6InterfaceError!u32 {
663 if (builtin.target.os.tag == .linux) {676 if (builtin.target.os.tag == .linux) {
664 var ifr: os.ifreq = undefined;677 var ifr: os.ifreq = undefined;
665 const sockfd = try os.socket(os.AF.UNIX, os.SOCK.DGRAM | os.SOCK.CLOEXEC, 0);678 const sockfd = try os.socket(os.AF.UNIX, os.SOCK.DGRAM | os.SOCK.CLOEXEC, 0);
lib/std/net/test.zig+22
...@@ -4,6 +4,28 @@ const net = std.net;...@@ -4,6 +4,28 @@ const net = std.net;
4const mem = std.mem;4const mem = std.mem;
5const testing = std.testing;5const testing = std.testing;
66
7test "parse and render IP addresses at comptime" {
8 if (builtin.os.tag == .wasi) return error.SkipZigTest;
9 comptime {
10 var ipAddrBuffer: [16]u8 = undefined;
11 // Parses IPv6 at comptime
12 const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable;
13 var ipv6 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv6addr}) catch unreachable;
14 try std.testing.expect(std.mem.eql(u8, "::1", ipv6[1 .. ipv6.len - 3]));
15
16 // Parses IPv4 at comptime
17 const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable;
18 var ipv4 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv4addr}) catch unreachable;
19 try std.testing.expect(std.mem.eql(u8, "127.0.0.1", ipv4[0 .. ipv4.len - 2]));
20
21 // Returns error for invalid IP addresses at comptime
22 try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("::123.123.123.123", 0));
23 try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("127.01.0.1", 0));
24 try testing.expectError(error.InvalidIPAddressFormat, net.Address.resolveIp("::123.123.123.123", 0));
25 try testing.expectError(error.InvalidIPAddressFormat, net.Address.resolveIp("127.01.0.1", 0));
26 }
27}
28
7test "parse and render IPv6 addresses" {29test "parse and render IPv6 addresses" {
8 if (builtin.os.tag == .wasi) return error.SkipZigTest;30 if (builtin.os.tag == .wasi) return error.SkipZigTest;
931