From b8163031220d94a78c378ff90317882d10dfe067 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 29 Mar 2020 16:28:53 -0300 Subject: [PATCH 01/25] Add basics of resolveIp6 Instead of streaming the scope id digits to an u32, we keep a [32]u8 in the stack and fill it up with the characters we get for scope id. --- lib/std/net.zig | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/lib/std/net.zig b/lib/std/net.zig index ea133b1e67..f757585f34 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -21,6 +21,9 @@ pub const Address = extern union { // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512 //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); + /// Parse the given IP address string into an Address value. + /// It is recommended to use Address.resolveIp instead, to handle + /// IPv6 link-local unix addresses. pub fn parseIp(name: []const u8, port: u16) !Address { if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { error.Overflow, @@ -42,6 +45,27 @@ pub const Address = extern union { return error.InvalidIPAddressFormat; } + pub fn resolveIp(name: []const u8, port: u16) !Address { + if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { + error.Overflow, + error.InvalidEnd, + error.InvalidCharacter, + error.Incomplete, + => {}, + } + + if (resolveIp6(name, port)) |ip6| return ip6 else |err| switch (err) { + error.Overflow, + error.InvalidEnd, + error.InvalidCharacter, + error.Incomplete, + error.InvalidIpv4Mapping, + => {}, + } + + return error.InvalidIPAddressFormat; + } + pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address { switch (family) { os.AF_INET => return parseIp4(name, port), @@ -157,6 +181,117 @@ pub const Address = extern union { } } + pub fn resolveIp6(buf: []const u8, port: u16) !Address { + // FIXME: implement if_nametoindex + // FIXME: this is a very bad implementation, since it's only a copy + // of parseIp6 with alphanumerical scope id support + var result = Address{ + .in6 = os.sockaddr_in6{ + .scope_id = 0, + .port = mem.nativeToBig(u16, port), + .flowinfo = 0, + .addr = undefined, + }, + }; + 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 abbrv = false; + + var scope_id = false; + var scope_id_value: [32]u8 = undefined; + var scope_id_index: usize = 0; + + for (buf) |c, i| { + if (scope_id) { + scope_id_value[scope_id_index] = c; + scope_id_index += 1; + } else if (c == ':') { + if (!saw_any_digits) { + 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; + } + ip_slice[index] = @truncate(u8, x >> 8); + index += 1; + ip_slice[index] = @truncate(u8, x); + index += 1; + + x = 0; + saw_any_digits = false; + } else if (c == '%') { + if (!saw_any_digits) { + return error.InvalidCharacter; + } + 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; + + const ptr = mem.sliceAsBytes(@as(*const [1]u32, &addr)[0..]); + + ip_slice[12] = ptr[0]; + ip_slice[13] = ptr[1]; + ip_slice[14] = ptr[2]; + ip_slice[15] = ptr[3]; + return result; + } else { + const digit = try std.fmt.charToDigit(c, 16); + if (@mulWithOverflow(u16, x, 16, &x)) { + return error.Overflow; + } + if (@addWithOverflow(u16, x, digit, &x)) { + return error.Overflow; + } + saw_any_digits = true; + } + } + + if (!saw_any_digits and !abbrv) { + return error.Incomplete; + } + + const resolved_scope_id = std.fmt.parseInt(u32, scope_id_value, 10) catch |err| blk: { + if (err != err.InvalidCharacter) return err; + break :blk if_nametoindex(scope_id_value); + } + + result.in6.scope_id = resolved_scope_id; + + 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; + } + } + pub fn parseIp4(buf: []const u8, port: u16) !Address { var result = Address{ .in = os.sockaddr_in{ From 9c200035f346c9687ccf9b490a704803a652138e Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 29 Mar 2020 17:17:09 -0300 Subject: [PATCH 02/25] Add some interface structs to linux bits --- lib/std/os/bits/linux.zig | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index dbdae19959..ccb04d30a2 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1705,3 +1705,35 @@ pub const termios = extern struct { ispeed: speed_t, ospeed: speed_t, }; + +pub const SIOCGIFINDEX = 0x8933; +pub const IFNAMESIZE = 16; + +pub const ifmap = struct { + mem_start: u32, + mem_end: u32, + base_addr: i16, + irq: u8, + dma: u8, + port: u8, +}; + +pub const ifreq = extern union { + ifr_ifrn: struct { + ifrn_name: [IFNAMESIZE]u8, + }, + ifr_ifru: struct { + ifru_addr: sockaddr, + ifru_dstaddr: sockaddr, + ifru_broadaddr: sockaddr, + ifru_netmask: sockaddr, + ifru_hwaddr: sockaddr, + ifru_flags: i16, + ifru_ivalue: i16, + ifru_mtu: i16, + ifru_map: ifmap, + ifru_slave: [IFNAMESIZE]u8, + ifru_newname: [IFNAMESIZE]u8, + ifru_data: [*:0]u8, + }, +}; From f02f4c08808c4f9f171825baf4a1b0d032f1e482 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 29 Mar 2020 17:17:40 -0300 Subject: [PATCH 03/25] Fix typo and add if_nametoindex --- lib/std/net.zig | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index f757585f34..768fbab2b2 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -75,6 +75,8 @@ pub const Address = extern union { } } + /// Parse a given IPv6 address string into an Address. + /// Assumes the Scope ID of the address is fully numeric. pub fn parseIp6(buf: []const u8, port: u16) !Address { var result = Address{ .in6 = os.sockaddr_in6{ @@ -274,7 +276,7 @@ pub const Address = extern union { const resolved_scope_id = std.fmt.parseInt(u32, scope_id_value, 10) catch |err| blk: { if (err != err.InvalidCharacter) return err; break :blk if_nametoindex(scope_id_value); - } + }; result.in6.scope_id = resolved_scope_id; @@ -515,6 +517,23 @@ pub fn connectUnixSocket(path: []const u8) !fs.File { }; } +fn if_nametoindex(name: []const u8) !u32 { + var ifr: os.ifreq = undefined; + var sockfd = try os.socket(os.AF_UNIX, os.SOCK_DGRAM | os.SOCK_CLOEXEC, 0); + defer os.close(sockfd); + + std.mem.copy(u8, ifr.ifr_ifrn.name, &name); + + const rc = os.system.syscall3( + os.linux.SYS_ioctl, + @bitCast(usize, @as(isize, sockfd)), + os.linux.SIOCGIFINDEX, + @ptrToInt(&ifr), + ); + + return ifr.ifr_ifru.ifru_ivalue; +} + pub const AddressList = struct { arena: std.heap.ArenaAllocator, addrs: []Address, From 2c641c93da1babed3d53bf705f3d6a84414512e7 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 29 Mar 2020 17:45:34 -0300 Subject: [PATCH 04/25] Only resolve scope id when needed --- lib/std/net.zig | 16 +++++++++------- lib/std/os/bits/linux.zig | 6 +++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 768fbab2b2..5cc9a8f740 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -184,7 +184,6 @@ pub const Address = extern union { } pub fn resolveIp6(buf: []const u8, port: u16) !Address { - // FIXME: implement if_nametoindex // FIXME: this is a very bad implementation, since it's only a copy // of parseIp6 with alphanumerical scope id support var result = Address{ @@ -205,7 +204,7 @@ pub const Address = extern union { var abbrv = false; var scope_id = false; - var scope_id_value: [32]u8 = undefined; + var scope_id_value: [16]u8 = undefined; var scope_id_index: usize = 0; for (buf) |c, i| { @@ -273,10 +272,13 @@ pub const Address = extern union { return error.Incomplete; } - const resolved_scope_id = std.fmt.parseInt(u32, scope_id_value, 10) catch |err| blk: { - if (err != err.InvalidCharacter) return err; - break :blk if_nametoindex(scope_id_value); - }; + var resolved_scope_id: u32 = 0; + if (std.mem.len(scope_id_value) > 0) { + resolved_scope_id = std.fmt.parseInt(u32, &scope_id_value, 10) catch |err| blk: { + if (err != error.InvalidCharacter) return err; + break :blk try if_nametoindex(&scope_id_value); + }; + } result.in6.scope_id = resolved_scope_id; @@ -522,7 +524,7 @@ fn if_nametoindex(name: []const u8) !u32 { var sockfd = try os.socket(os.AF_UNIX, os.SOCK_DGRAM | os.SOCK_CLOEXEC, 0); defer os.close(sockfd); - std.mem.copy(u8, ifr.ifr_ifrn.name, &name); + std.mem.copy(u8, &ifr.ifr_ifrn.name, name); const rc = os.system.syscall3( os.linux.SYS_ioctl, diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index ccb04d30a2..773a61b3ce 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1720,7 +1720,7 @@ pub const ifmap = struct { pub const ifreq = extern union { ifr_ifrn: struct { - ifrn_name: [IFNAMESIZE]u8, + name: [IFNAMESIZE]u8, }, ifr_ifru: struct { ifru_addr: sockaddr, @@ -1729,8 +1729,8 @@ pub const ifreq = extern union { ifru_netmask: sockaddr, ifru_hwaddr: sockaddr, ifru_flags: i16, - ifru_ivalue: i16, - ifru_mtu: i16, + ifru_ivalue: i32, + ifru_mtu: i32, ifru_map: ifmap, ifru_slave: [IFNAMESIZE]u8, ifru_newname: [IFNAMESIZE]u8, From 901aab8761daa5c2dae5e470d51f5b3bf76e2b78 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 29 Mar 2020 17:45:43 -0300 Subject: [PATCH 05/25] Add ioctl errors --- lib/std/net.zig | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 5cc9a8f740..78ed8bc913 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -533,7 +533,18 @@ fn if_nametoindex(name: []const u8) !u32 { @ptrToInt(&ifr), ); - return ifr.ifr_ifru.ifru_ivalue; + switch (os.errno(rc)) { + os.EBADF => return error.BadFile, + os.EINTR => return error.CaughtSignal, + os.EINVAL => unreachable, + os.EIO => return error.FileSystem, + os.ENOTTY => unreachable, + os.ENXIO => unreachable, + os.ENODEV => return error.Unsupported, + else => {}, + } + + return @bitCast(u32, ifr.ifr_ifru.ifru_ivalue); } pub const AddressList = struct { From 5919831529418ddf019b69e13ccccfce64837638 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 30 Mar 2020 16:01:22 -0300 Subject: [PATCH 06/25] Stop using mem.len on array --- lib/std/net.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 78ed8bc913..011149f09b 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -273,10 +273,12 @@ pub const Address = extern union { } var resolved_scope_id: u32 = 0; - if (std.mem.len(scope_id_value) > 0) { - resolved_scope_id = std.fmt.parseInt(u32, &scope_id_value, 10) catch |err| blk: { + std.debug.warn("scope_id_value {} len {}\n", .{ scope_id_value, std.mem.len(scope_id_value) }); + if (scope_id_index > 0) { + const scope_id_str = scope_id_value[0..scope_id_index]; + resolved_scope_id = std.fmt.parseInt(u32, scope_id_str, 10) catch |err| blk: { if (err != error.InvalidCharacter) return err; - break :blk try if_nametoindex(&scope_id_value); + break :blk try if_nametoindex(scope_id_str); }; } From 38109d48a369b3ab2409287317d18929ce8725b2 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 30 Mar 2020 16:04:50 -0300 Subject: [PATCH 07/25] Make interface name null-terminated before syscall --- lib/std/net.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/std/net.zig b/lib/std/net.zig index 011149f09b..b4e2254da4 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -527,6 +527,10 @@ fn if_nametoindex(name: []const u8) !u32 { defer os.close(sockfd); std.mem.copy(u8, &ifr.ifr_ifrn.name, name); + std.debug.warn("name={} name.len={} ifr_name={}\n", .{ name, name.len, ifr.ifr_ifrn.name }); + ifr.ifr_ifrn.name[name.len] = 0; + + std.debug.warn("{} {} {}\n", .{ sockfd, os.linux.SIOCGIFINDEX, @ptrToInt(&ifr) }); const rc = os.system.syscall3( os.linux.SYS_ioctl, From c50ac9a764c9022e69c9885b17083d70223cb1aa Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 30 Mar 2020 16:05:22 -0300 Subject: [PATCH 08/25] Change Unsupported to InterfaceNotFound --- lib/std/net.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index b4e2254da4..1596df2d7e 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -542,14 +542,17 @@ fn if_nametoindex(name: []const u8) !u32 { switch (os.errno(rc)) { os.EBADF => return error.BadFile, os.EINTR => return error.CaughtSignal, - os.EINVAL => unreachable, os.EIO => return error.FileSystem, + os.EINVAL => unreachable, os.ENOTTY => unreachable, os.ENXIO => unreachable, - os.ENODEV => return error.Unsupported, + // ioctl() sends ENODEV for an unknown scope id. + os.ENODEV => return error.InterfaceNotFound, else => {}, } + std.debug.warn("ival={}\n", .{ifr.ifr_ifru.ifru_ivalue}); + return @bitCast(u32, ifr.ifr_ifru.ifru_ivalue); } From cb649b769c4a8d1d5d703994976c2c9e4ae4e03b Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 30 Mar 2020 17:06:45 -0300 Subject: [PATCH 09/25] Fix ifreq definition --- lib/std/net.zig | 2 +- lib/std/os/bits/linux.zig | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 1596df2d7e..0bb797fd5e 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -551,7 +551,7 @@ fn if_nametoindex(name: []const u8) !u32 { else => {}, } - std.debug.warn("ival={}\n", .{ifr.ifr_ifru.ifru_ivalue}); + std.debug.warn("ival={}, rest={}\n", .{ ifr.ifr_ifru.ifru_ivalue, ifr.ifr_ifru }); return @bitCast(u32, ifr.ifr_ifru.ifru_ivalue); } diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 773a61b3ce..97ca3e94e4 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1709,31 +1709,31 @@ pub const termios = extern struct { pub const SIOCGIFINDEX = 0x8933; pub const IFNAMESIZE = 16; -pub const ifmap = struct { - mem_start: u32, - mem_end: u32, - base_addr: i16, +pub const ifmap = extern struct { + mem_start: c_ulong, + mem_end: c_ulong, + base_addr: c_ushort, irq: u8, dma: u8, port: u8, }; -pub const ifreq = extern union { - ifr_ifrn: struct { +pub const ifreq = extern struct { + ifr_ifrn: extern union { name: [IFNAMESIZE]u8, }, - ifr_ifru: struct { + ifr_ifru: extern union { ifru_addr: sockaddr, ifru_dstaddr: sockaddr, ifru_broadaddr: sockaddr, ifru_netmask: sockaddr, ifru_hwaddr: sockaddr, - ifru_flags: i16, + ifru_flags: c_short, ifru_ivalue: i32, ifru_mtu: i32, ifru_map: ifmap, ifru_slave: [IFNAMESIZE]u8, ifru_newname: [IFNAMESIZE]u8, - ifru_data: [*:0]u8, + ifru_data: [*c]u8, }, }; From 11a06d4341a704d0f0c67803f12a3ac0706af3ad Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 30 Mar 2020 17:10:00 -0300 Subject: [PATCH 10/25] Remove warn() calls --- lib/std/net.zig | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 0bb797fd5e..9c7d8f2854 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -273,7 +273,6 @@ pub const Address = extern union { } var resolved_scope_id: u32 = 0; - std.debug.warn("scope_id_value {} len {}\n", .{ scope_id_value, std.mem.len(scope_id_value) }); if (scope_id_index > 0) { const scope_id_str = scope_id_value[0..scope_id_index]; resolved_scope_id = std.fmt.parseInt(u32, scope_id_str, 10) catch |err| blk: { @@ -527,11 +526,8 @@ fn if_nametoindex(name: []const u8) !u32 { defer os.close(sockfd); std.mem.copy(u8, &ifr.ifr_ifrn.name, name); - std.debug.warn("name={} name.len={} ifr_name={}\n", .{ name, name.len, ifr.ifr_ifrn.name }); ifr.ifr_ifrn.name[name.len] = 0; - std.debug.warn("{} {} {}\n", .{ sockfd, os.linux.SIOCGIFINDEX, @ptrToInt(&ifr) }); - const rc = os.system.syscall3( os.linux.SYS_ioctl, @bitCast(usize, @as(isize, sockfd)), @@ -551,8 +547,6 @@ fn if_nametoindex(name: []const u8) !u32 { else => {}, } - std.debug.warn("ival={}, rest={}\n", .{ ifr.ifr_ifru.ifru_ivalue, ifr.ifr_ifru }); - return @bitCast(u32, ifr.ifr_ifru.ifru_ivalue); } From 10ea2db5cb37adf248b04e8c8b46cbb97b4bda4e Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 30 Mar 2020 17:23:46 -0300 Subject: [PATCH 11/25] Replace C shorts by integer types --- lib/std/os/bits/linux.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 97ca3e94e4..0a12d13074 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1712,7 +1712,7 @@ pub const IFNAMESIZE = 16; pub const ifmap = extern struct { mem_start: c_ulong, mem_end: c_ulong, - base_addr: c_ushort, + base_addr: u16, irq: u8, dma: u8, port: u8, @@ -1728,7 +1728,7 @@ pub const ifreq = extern struct { ifru_broadaddr: sockaddr, ifru_netmask: sockaddr, ifru_hwaddr: sockaddr, - ifru_flags: c_short, + ifru_flags: i16, ifru_ivalue: i32, ifru_mtu: i32, ifru_map: ifmap, From 64e55a74de8229f0a1cf5a174746e265d56c16fa Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 2 Apr 2020 17:34:05 -0300 Subject: [PATCH 12/25] Add validation for scope ids --- lib/std/net.zig | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 9c7d8f2854..96442b9933 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -209,8 +209,17 @@ pub const Address = extern union { for (buf) |c, i| { if (scope_id) { - scope_id_value[scope_id_index] = c; - scope_id_index += 1; + // Handling of percent-encoding should be for an URI library. + if ((c >= '0' and c <= '9') or + (c >= 'A' and c <= 'Z') or + (c >= 'a' and c <= 'z') or + (c == '-') or (c == '.') or (c == '_') or (c == '~')) + { + scope_id_value[scope_id_index] = c; + scope_id_index += 1; + } else { + return error.InvalidCharacter; + } } else if (c == ':') { if (!saw_any_digits) { if (abbrv) return error.InvalidCharacter; // ':::' @@ -272,6 +281,10 @@ pub const Address = extern union { return error.Incomplete; } + if (scope_id and scope_id_index == 0) { + return error.Incomplete; + } + var resolved_scope_id: u32 = 0; if (scope_id_index > 0) { const scope_id_str = scope_id_value[0..scope_id_index]; From 2fa9cf51ffc6c4be4f06c46616e4771ac05ba599 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 2 Apr 2020 17:34:25 -0300 Subject: [PATCH 13/25] Plug resolveIp6 into IPv6 tests --- lib/std/net/test.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 8485f049a8..91fff855c1 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -32,8 +32,13 @@ test "parse and render IPv6 addresses" { }; for (ips) |ip, i| { var addr = net.Address.parseIp6(ip, 0) catch unreachable; + var addr_via_resolve = net.Address.resolveIp6(ip, 0) catch unreachable; + var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; + var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr_via_resolve}) catch unreachable; + std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); + std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3])); } testing.expectError(error.InvalidCharacter, net.Address.parseIp6(":::", 0)); @@ -42,6 +47,7 @@ test "parse and render IPv6 addresses" { testing.expectError(error.InvalidEnd, net.Address.parseIp6("FF01:0:0:0:0:0:0:FB:", 0)); testing.expectError(error.Incomplete, net.Address.parseIp6("FF01:", 0)); testing.expectError(error.InvalidIpv4Mapping, net.Address.parseIp6("::123.123.123.123", 0)); + testing.expectError(error.Incomplete, net.Address.resolveIp6("ff01::fb%", 0)); } test "parse and render IPv4 addresses" { From c60daa255f447a603af2b33b13c1188dd8fccc94 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 2 Apr 2020 17:37:29 -0300 Subject: [PATCH 14/25] Replace C types in declarations --- lib/std/os/bits/linux.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 0a12d13074..06f033c721 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1710,8 +1710,8 @@ pub const SIOCGIFINDEX = 0x8933; pub const IFNAMESIZE = 16; pub const ifmap = extern struct { - mem_start: c_ulong, - mem_end: c_ulong, + mem_start: u32, + mem_end: u32, base_addr: u16, irq: u8, dma: u8, @@ -1734,6 +1734,6 @@ pub const ifreq = extern struct { ifru_map: ifmap, ifru_slave: [IFNAMESIZE]u8, ifru_newname: [IFNAMESIZE]u8, - ifru_data: [*c]u8, + ifru_data: ?[*]u8, }, }; From aebf28eba313f6472395bd3fbba3f79dba5326bd Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 2 Apr 2020 18:12:39 -0300 Subject: [PATCH 15/25] Make ifru fields sentinel-terminated --- lib/std/os/bits/linux.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 06f033c721..1a8ff84ac0 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1732,8 +1732,8 @@ pub const ifreq = extern struct { ifru_ivalue: i32, ifru_mtu: i32, ifru_map: ifmap, - ifru_slave: [IFNAMESIZE]u8, - ifru_newname: [IFNAMESIZE]u8, + ifru_slave: [IFNAMESIZE - 1:0]u8, + ifru_newname: [IFNAMESIZE - 1:0]u8, ifru_data: ?[*]u8, }, }; From b72c862725f6005250cb906e96d41bd1b80fd7ca Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 2 Apr 2020 18:13:10 -0300 Subject: [PATCH 16/25] Use IFNAMESIZE for scope id value --- lib/std/net.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 96442b9933..866a7b0cca 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -204,7 +204,7 @@ pub const Address = extern union { var abbrv = false; var scope_id = false; - var scope_id_value: [16]u8 = undefined; + var scope_id_value: [os.IFNAMESIZE - 1]u8 = undefined; var scope_id_index: usize = 0; for (buf) |c, i| { @@ -215,6 +215,10 @@ pub const Address = extern union { (c >= 'a' and c <= 'z') or (c == '-') or (c == '.') or (c == '_') or (c == '~')) { + if (scope_id_index >= scope_id_value.len) { + return error.Overflow; + } + scope_id_value[scope_id_index] = c; scope_id_index += 1; } else { From 15efe03d1f967fe8fe3731ac16ac802256605d22 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 2 Apr 2020 18:13:39 -0300 Subject: [PATCH 17/25] Add tests for overflow --- lib/std/net/test.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 91fff855c1..909d6318b3 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -48,6 +48,17 @@ test "parse and render IPv6 addresses" { testing.expectError(error.Incomplete, net.Address.parseIp6("FF01:", 0)); testing.expectError(error.InvalidIpv4Mapping, net.Address.parseIp6("::123.123.123.123", 0)); testing.expectError(error.Incomplete, net.Address.resolveIp6("ff01::fb%", 0)); + testing.expectError(error.Overflow, net.Address.resolveIp6("ff01::fb%wlp3s0s0s0s0s0s0s0s0", 0)); + testing.expectError(error.Overflow, net.Address.resolveIp6("ff01::fb%12345678901234", 0)); +} + +test "invalid but parseable IPv6 scope ids" { + // Currently, resolveIp6 with alphanumerical scope IDs only works on Linux. + if (std.builtin.os.tag != .linux) { + return error.SkipZigTest; + } + + testing.expectError(error.InterfaceNotFound, net.Address.resolveIp6("ff01::fb%123s45678901234", 0)); } test "parse and render IPv4 addresses" { From c7b790ded685cb3bcb17fe7a27de7ae054133250 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 9 Apr 2020 22:13:36 -0300 Subject: [PATCH 18/25] net.test: only call resolveIp6 when os is linux --- lib/std/net/test.zig | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 909d6318b3..cec9185eae 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -32,13 +32,14 @@ test "parse and render IPv6 addresses" { }; for (ips) |ip, i| { var addr = net.Address.parseIp6(ip, 0) catch unreachable; - var addr_via_resolve = net.Address.resolveIp6(ip, 0) catch unreachable; - var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; - var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr_via_resolve}) catch unreachable; - std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); - std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3])); + + if (std.builtin.os.tag == .linux) { + var addr_via_resolve = net.Address.resolveIp6(ip, 0) catch unreachable; + var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr_via_resolve}) catch unreachable; + std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3])); + } } testing.expectError(error.InvalidCharacter, net.Address.parseIp6(":::", 0)); @@ -47,9 +48,11 @@ test "parse and render IPv6 addresses" { testing.expectError(error.InvalidEnd, net.Address.parseIp6("FF01:0:0:0:0:0:0:FB:", 0)); testing.expectError(error.Incomplete, net.Address.parseIp6("FF01:", 0)); testing.expectError(error.InvalidIpv4Mapping, net.Address.parseIp6("::123.123.123.123", 0)); - testing.expectError(error.Incomplete, net.Address.resolveIp6("ff01::fb%", 0)); - testing.expectError(error.Overflow, net.Address.resolveIp6("ff01::fb%wlp3s0s0s0s0s0s0s0s0", 0)); - testing.expectError(error.Overflow, net.Address.resolveIp6("ff01::fb%12345678901234", 0)); + if (std.builtin.os.tag == .linux) { + testing.expectError(error.Incomplete, net.Address.resolveIp6("ff01::fb%", 0)); + testing.expectError(error.Overflow, net.Address.resolveIp6("ff01::fb%wlp3s0s0s0s0s0s0s0s0", 0)); + testing.expectError(error.Overflow, net.Address.resolveIp6("ff01::fb%12345678901234", 0)); + } } test "invalid but parseable IPv6 scope ids" { From c8468bed42db773cc0622c6ea2e18d5e17808f2b Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 20 Apr 2020 16:34:37 -0300 Subject: [PATCH 19/25] Add std.os.ioctl --- lib/std/net.zig | 24 ++++++------------------ lib/std/os.zig | 24 ++++++++++++++++++------ lib/std/os/linux.zig | 4 ++++ 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 866a7b0cca..22c3d6d895 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -545,24 +545,12 @@ fn if_nametoindex(name: []const u8) !u32 { std.mem.copy(u8, &ifr.ifr_ifrn.name, name); ifr.ifr_ifrn.name[name.len] = 0; - const rc = os.system.syscall3( - os.linux.SYS_ioctl, - @bitCast(usize, @as(isize, sockfd)), - os.linux.SIOCGIFINDEX, - @ptrToInt(&ifr), - ); - - switch (os.errno(rc)) { - os.EBADF => return error.BadFile, - os.EINTR => return error.CaughtSignal, - os.EIO => return error.FileSystem, - os.EINVAL => unreachable, - os.ENOTTY => unreachable, - os.ENXIO => unreachable, - // ioctl() sends ENODEV for an unknown scope id. - os.ENODEV => return error.InterfaceNotFound, - else => {}, - } + std.os.ioctl(sockfd, os.linux.SIOCGIFINDEX, @ptrToInt(&ifr)) catch |err| { + switch (err) { + error.NoDevice => return error.InterfaceNotFound, + else => return err, + } + }; return @bitCast(u32, ifr.ifr_ifru.ifru_ivalue); } diff --git a/lib/std/os.zig b/lib/std/os.zig index 24cf527040..afa161d5b4 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -405,7 +405,6 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { else => |err| return unexpectedErrno(err), } } - const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31); while (true) { // TODO handle the case when iov_len is too large and get rid of this @intCast @@ -2451,9 +2450,8 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t if (builtin.os.tag == .windows) { // NOTE: windows translates the SOCK_NONBLOCK/SOCK_CLOEXEC flags into windows-analagous operations const filtered_sock_type = socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC); - const flags : u32 = if ((socket_type & SOCK_CLOEXEC) != 0) windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT else 0; - const rc = windows.ws2_32.WSASocketW(@intCast(c_int, domain), @intCast(c_int, filtered_sock_type), - @intCast(c_int, protocol), null, 0, flags); + const flags: u32 = if ((socket_type & SOCK_CLOEXEC) != 0) windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT else 0; + const rc = windows.ws2_32.WSASocketW(@intCast(c_int, domain), @intCast(c_int, filtered_sock_type), @intCast(c_int, protocol), null, 0, flags); if (rc == windows.ws2_32.INVALID_SOCKET) switch (windows.ws2_32.WSAGetLastError()) { .WSAEMFILE => return error.ProcessFdQuotaExceeded, .WSAENOBUFS => return error.SystemResources, @@ -2463,7 +2461,7 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t }; errdefer windows.closesocket(rc) catch unreachable; if ((socket_type & SOCK_NONBLOCK) != 0) { - var mode : c_ulong = 1; // nonblocking + var mode: c_ulong = 1; // nonblocking if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) { switch (windows.ws2_32.WSAGetLastError()) { // have not identified any error codes that should be handled yet @@ -2858,7 +2856,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con .WSAECONNREFUSED => return error.ConnectionRefused, .WSAETIMEDOUT => return error.ConnectionTimedOut, .WSAEHOSTUNREACH // TODO: should we return NetworkUnreachable in this case as well? - ,.WSAENETUNREACH => return error.NetworkUnreachable, + , .WSAENETUNREACH => return error.NetworkUnreachable, .WSAEFAULT => unreachable, .WSAEINVAL => unreachable, .WSAEISCONN => unreachable, @@ -4906,3 +4904,17 @@ pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) Termio } } } + +pub fn ioctl(handle: fd_t, request: u32, arg: var) !void { + switch (errno(system.ioctl(handle, request, arg))) { + 0 => {}, + EINVAL => unreachable, + ENOTTY => unreachable, + ENXIO => unreachable, + EBADF => return error.BadFile, + EINTR => return error.CaughtSignal, + EIO => return error.FileSystem, + ENODEV => return error.NoDevice, + else => |err| return unexpectedErrno(err), + } +} diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index f00238141a..db901594f7 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1193,6 +1193,10 @@ pub fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) usi return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), TCSETS + @enumToInt(optional_action), @ptrToInt(termios_p)); } +pub fn ioctl(fd: fd_t, request: u32, arg: var) usize { + return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), request, arg); +} + test "" { if (builtin.os.tag == .linux) { _ = @import("linux/test.zig"); From 09c01ea7b9a8d80f3ad0eec1e20303996fdcba9b Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 20 Apr 2020 16:37:16 -0300 Subject: [PATCH 20/25] Use resolveIp when looking up addresses on linux --- lib/std/net.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 22c3d6d895..9e3baeb3b5 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -61,6 +61,7 @@ pub const Address = extern union { error.Incomplete, error.InvalidIpv4Mapping, => {}, + else => return err, } return error.InvalidIPAddressFormat; @@ -545,7 +546,7 @@ fn if_nametoindex(name: []const u8) !u32 { std.mem.copy(u8, &ifr.ifr_ifrn.name, name); ifr.ifr_ifrn.name[name.len] = 0; - std.os.ioctl(sockfd, os.linux.SIOCGIFINDEX, @ptrToInt(&ifr)) catch |err| { + os.ioctl(sockfd, os.linux.SIOCGIFINDEX, @ptrToInt(&ifr)) catch |err| { switch (err) { error.NoDevice => return error.InterfaceNotFound, else => return err, @@ -1246,7 +1247,7 @@ fn linuxLookupNameFromNumericUnspec( name: []const u8, port: u16, ) !void { - const addr = try Address.parseIp(name, port); + const addr = try Address.resolveIp(name, port); (try addrs.addOne()).* = LookupAddr{ .addr = addr }; } From 7c710542867d283b6ab774af76083d55996b127e Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 20 Apr 2020 17:16:52 -0300 Subject: [PATCH 21/25] Replace syscall3 to os.ioctl --- lib/std/os.zig | 2 +- lib/std/os/linux.zig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index afa161d5b4..d82c1da3d0 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2391,7 +2391,7 @@ pub fn isatty(handle: fd_t) bool { } if (builtin.os.tag == .linux) { var wsz: linux.winsize = undefined; - return linux.syscall3(.ioctl, @bitCast(usize, @as(isize, handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0; + return linux.ioctl(handle, linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } unreachable; } diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index db901594f7..b7ba8f3636 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1186,11 +1186,11 @@ pub fn getrusage(who: i32, usage: *rusage) usize { } pub fn tcgetattr(fd: fd_t, termios_p: *termios) usize { - return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), TCGETS, @ptrToInt(termios_p)); + return ioctl(fd, TCGETS, @ptrToInt(termios_p)); } pub fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) usize { - return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), TCSETS + @enumToInt(optional_action), @ptrToInt(termios_p)); + return ioctl(fd, TCSETS + @enumToInt(optional_action), @ptrToInt(termios_p)); } pub fn ioctl(fd: fd_t, request: u32, arg: var) usize { From 6d3d1152eafea83efcde066d494edd9adc65a02d Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 20 Apr 2020 17:47:05 -0300 Subject: [PATCH 22/25] Add declaration for libc ioctl --- lib/std/c.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/c.zig b/lib/std/c.zig index 2aeb30c875..fe9fc7ac40 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -132,6 +132,7 @@ pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int; pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int; pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int; pub extern "c" fn flock(fd: fd_t, operation: c_int) c_int; +pub extern "c" fn ioctl(fd: fd_t, request: c_int, ...) c_int; pub extern "c" fn uname(buf: *utsname) c_int; pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int; From 6623efd7d4ae3f71f7da4c8aa9975291c1d89303 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 20 Apr 2020 18:03:54 -0300 Subject: [PATCH 23/25] Change ioctl's request type to i32 --- lib/std/os.zig | 2 +- lib/std/os/linux.zig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index d82c1da3d0..497dd0f8dd 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4905,7 +4905,7 @@ pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) Termio } } -pub fn ioctl(handle: fd_t, request: u32, arg: var) !void { +pub fn ioctl(handle: fd_t, request: i32, arg: var) !void { switch (errno(system.ioctl(handle, request, arg))) { 0 => {}, EINVAL => unreachable, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index b7ba8f3636..08739dc1d9 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1193,8 +1193,8 @@ pub fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) usi return ioctl(fd, TCSETS + @enumToInt(optional_action), @ptrToInt(termios_p)); } -pub fn ioctl(fd: fd_t, request: u32, arg: var) usize { - return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), request, arg); +pub fn ioctl(fd: fd_t, request: i32, arg: var) usize { + return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, request)), arg); } test "" { From 0d091dc92374a49c41d406b5bf9a4b508a3d450c Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 20 Apr 2020 19:56:45 -0300 Subject: [PATCH 24/25] Replace os.linux to os.system --- lib/std/net.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 9e3baeb3b5..a0bc99f1a5 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -546,7 +546,7 @@ fn if_nametoindex(name: []const u8) !u32 { std.mem.copy(u8, &ifr.ifr_ifrn.name, name); ifr.ifr_ifrn.name[name.len] = 0; - os.ioctl(sockfd, os.linux.SIOCGIFINDEX, @ptrToInt(&ifr)) catch |err| { + os.ioctl(sockfd, os.system.SIOCGIFINDEX, @ptrToInt(&ifr)) catch |err| { switch (err) { error.NoDevice => return error.InterfaceNotFound, else => return err, From 7fd937fef4547a98d7c33ea67eca76e6336f9152 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 2 Jun 2020 15:28:46 -0400 Subject: [PATCH 25/25] cleanups * improve docs * add TODO comments for things that don't have open issues * remove redundant namespacing of struct fields * guard against ioctl returning EINTR * remove the general std.os.ioctl function in favor of the specific ioctl_SIOCGIFINDEX function. This allows us to have a more precise error set, and more type-safe API. --- lib/std/net.zig | 20 ++++++-------- lib/std/net/test.zig | 4 ++- lib/std/os.zig | 56 ++++++++++++++++++++++++++------------- lib/std/os/bits/linux.zig | 28 ++++++++++---------- lib/std/os/linux.zig | 8 +++--- 5 files changed, 66 insertions(+), 50 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index a0bc99f1a5..34af916d13 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -22,7 +22,7 @@ pub const Address = extern union { //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); /// Parse the given IP address string into an Address value. - /// It is recommended to use Address.resolveIp instead, to handle + /// It is recommended to use `resolveIp` instead, to handle /// IPv6 link-local unix addresses. pub fn parseIp(name: []const u8, port: u16) !Address { if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { @@ -78,6 +78,7 @@ pub const Address = extern union { /// 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 parseIp6(buf: []const u8, port: u16) !Address { var result = Address{ .in6 = os.sockaddr_in6{ @@ -185,8 +186,7 @@ pub const Address = extern union { } pub fn resolveIp6(buf: []const u8, port: u16) !Address { - // FIXME: this is a very bad implementation, since it's only a copy - // of parseIp6 with alphanumerical scope id support + // TODO: Unify the implementations of resolveIp6 and parseIp6. var result = Address{ .in6 = os.sockaddr_in6{ .scope_id = 0, @@ -543,17 +543,13 @@ fn if_nametoindex(name: []const u8) !u32 { var sockfd = try os.socket(os.AF_UNIX, os.SOCK_DGRAM | os.SOCK_CLOEXEC, 0); defer os.close(sockfd); - std.mem.copy(u8, &ifr.ifr_ifrn.name, name); - ifr.ifr_ifrn.name[name.len] = 0; + std.mem.copy(u8, &ifr.ifrn.name, name); + ifr.ifrn.name[name.len] = 0; - os.ioctl(sockfd, os.system.SIOCGIFINDEX, @ptrToInt(&ifr)) catch |err| { - switch (err) { - error.NoDevice => return error.InterfaceNotFound, - else => return err, - } - }; + // TODO investigate if this needs to be integrated with evented I/O. + try os.ioctl_SIOCGIFINDEX(sockfd, &ifr); - return @bitCast(u32, ifr.ifr_ifru.ifru_ivalue); + return @bitCast(u32, ifr.ifru.ivalue); } pub const AddressList = struct { diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index cec9185eae..5327e88ffc 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -48,6 +48,7 @@ test "parse and render IPv6 addresses" { testing.expectError(error.InvalidEnd, net.Address.parseIp6("FF01:0:0:0:0:0:0:FB:", 0)); testing.expectError(error.Incomplete, net.Address.parseIp6("FF01:", 0)); testing.expectError(error.InvalidIpv4Mapping, net.Address.parseIp6("::123.123.123.123", 0)); + // TODO Make this test pass on other operating systems. if (std.builtin.os.tag == .linux) { testing.expectError(error.Incomplete, net.Address.resolveIp6("ff01::fb%", 0)); testing.expectError(error.Overflow, net.Address.resolveIp6("ff01::fb%wlp3s0s0s0s0s0s0s0s0", 0)); @@ -56,8 +57,9 @@ test "parse and render IPv6 addresses" { } test "invalid but parseable IPv6 scope ids" { - // Currently, resolveIp6 with alphanumerical scope IDs only works on Linux. if (std.builtin.os.tag != .linux) { + // Currently, resolveIp6 with alphanumerical scope IDs only works on Linux. + // TODO Make this test pass on other operating systems. return error.SkipZigTest; } diff --git a/lib/std/os.zig b/lib/std/os.zig index 497dd0f8dd..c56f6e0db0 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2390,8 +2390,15 @@ pub fn isatty(handle: fd_t) bool { return true; } if (builtin.os.tag == .linux) { - var wsz: linux.winsize = undefined; - return linux.ioctl(handle, linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0; + while (true) { + var wsz: linux.winsize = undefined; + const fd = @bitCast(usize, @as(isize, handle)); + switch (linux.syscall3(.ioctl, fd, linux.TIOCGWINSZ, @ptrToInt(&wsz))) { + 0 => return true, + EINTR => continue, + else => return false, + } + } } unreachable; } @@ -4880,12 +4887,15 @@ pub fn getrusage(who: i32) rusage { pub const TermiosGetError = error{NotATerminal} || UnexpectedError; pub fn tcgetattr(handle: fd_t) TermiosGetError!termios { - var term: termios = undefined; - switch (errno(system.tcgetattr(handle, &term))) { - 0 => return term, - EBADF => unreachable, - ENOTTY => return error.NotATerminal, - else => |err| return unexpectedErrno(err), + while (true) { + var term: termios = undefined; + switch (errno(system.tcgetattr(handle, &term))) { + 0 => return term, + EINTR => continue, + EBADF => unreachable, + ENOTTY => return error.NotATerminal, + else => |err| return unexpectedErrno(err), + } } } @@ -4905,16 +4915,24 @@ pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) Termio } } -pub fn ioctl(handle: fd_t, request: i32, arg: var) !void { - switch (errno(system.ioctl(handle, request, arg))) { - 0 => {}, - EINVAL => unreachable, - ENOTTY => unreachable, - ENXIO => unreachable, - EBADF => return error.BadFile, - EINTR => return error.CaughtSignal, - EIO => return error.FileSystem, - ENODEV => return error.NoDevice, - else => |err| return unexpectedErrno(err), +const IoCtl_SIOCGIFINDEX_Error = error{ + FileSystem, + InterfaceNotFound, +} || UnexpectedError; + +pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void { + while (true) { + switch (errno(system.ioctl(fd, SIOCGIFINDEX, @ptrToInt(ifr)))) { + 0 => return, + EINVAL => unreachable, // Bad parameters. + ENOTTY => unreachable, + ENXIO => unreachable, + EBADF => unreachable, // Always a race condition. + EFAULT => unreachable, // Bad pointer parameter. + EINTR => continue, + EIO => return error.FileSystem, + ENODEV => return error.InterfaceNotFound, + else => |err| return unexpectedErrno(err), + } } } diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 1a8ff84ac0..64832673f1 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1719,21 +1719,21 @@ pub const ifmap = extern struct { }; pub const ifreq = extern struct { - ifr_ifrn: extern union { + ifrn: extern union { name: [IFNAMESIZE]u8, }, - ifr_ifru: extern union { - ifru_addr: sockaddr, - ifru_dstaddr: sockaddr, - ifru_broadaddr: sockaddr, - ifru_netmask: sockaddr, - ifru_hwaddr: sockaddr, - ifru_flags: i16, - ifru_ivalue: i32, - ifru_mtu: i32, - ifru_map: ifmap, - ifru_slave: [IFNAMESIZE - 1:0]u8, - ifru_newname: [IFNAMESIZE - 1:0]u8, - ifru_data: ?[*]u8, + ifru: extern union { + addr: sockaddr, + dstaddr: sockaddr, + broadaddr: sockaddr, + netmask: sockaddr, + hwaddr: sockaddr, + flags: i16, + ivalue: i32, + mtu: i32, + map: ifmap, + slave: [IFNAMESIZE - 1:0]u8, + newname: [IFNAMESIZE - 1:0]u8, + data: ?[*]u8, }, }; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 08739dc1d9..3b8df3d173 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1186,15 +1186,15 @@ pub fn getrusage(who: i32, usage: *rusage) usize { } pub fn tcgetattr(fd: fd_t, termios_p: *termios) usize { - return ioctl(fd, TCGETS, @ptrToInt(termios_p)); + return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), TCGETS, @ptrToInt(termios_p)); } pub fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) usize { - return ioctl(fd, TCSETS + @enumToInt(optional_action), @ptrToInt(termios_p)); + return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), TCSETS + @enumToInt(optional_action), @ptrToInt(termios_p)); } -pub fn ioctl(fd: fd_t, request: i32, arg: var) usize { - return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, request)), arg); +pub fn ioctl(fd: fd_t, request: u32, arg: usize) usize { + return syscall3(.ioctl, @bitCast(usize, @as(isize, fd)), request, arg); } test "" {