Commit d776a6bbbe
Changed files (3)
lib
std
lib/std/Io/net/HostName.zig
@@ -105,11 +105,11 @@ pub fn lookup(host_name: HostName, io: Io, options: LookupOptions) LookupError!L
{
var i: usize = 0;
if (options.family != .ip6) {
- options.addresses_buffer[i] = .{ .ip4 = .localhost(options.port) };
+ options.addresses_buffer[i] = .{ .ip4 = .loopback(options.port) };
i += 1;
}
if (options.family != .ip4) {
- options.addresses_buffer[i] = .{ .ip6 = .localhost(options.port) };
+ options.addresses_buffer[i] = .{ .ip6 = .loopback(options.port) };
i += 1;
}
const canon_name = "localhost";
@@ -166,7 +166,7 @@ fn sortLookupResults(options: LookupOptions, result: LookupResult) !LookupResult
switch (a) {
.ip6 => |ip6| {
da6.bytes = ip6.bytes;
- da6.scope_id = ip6.scope_id;
+ da6.interface = ip6.interface;
},
.ip4 => |ip4| {
da6.bytes[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
lib/std/Io/net.zig
@@ -26,10 +26,12 @@ pub const IpAddress = union(enum) {
pub const Family = @typeInfo(IpAddress).@"union".tag_type.?;
/// Parse the given IP address string into an `IpAddress` value.
+ ///
+ /// This is a pure function but it cannot handle IPv6 addresses that have
+ /// scope ids ("%foo" at the end). To also handle those, `resolve` must be
+ /// called instead.
pub fn parse(name: []const u8, port: u16) !IpAddress {
- if (Ip4Address.parse(name, port)) |ip4| {
- return .{ .ip4 = ip4 };
- } else |err| switch (err) {
+ if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) {
error.Overflow,
error.InvalidEnd,
error.InvalidCharacter,
@@ -38,26 +40,41 @@ pub const IpAddress = union(enum) {
=> {},
}
- if (Ip6Address.parse(name, port)) |ip6| {
- return .{ .ip6 = ip6 };
- } else |err| switch (err) {
+ return parseIp6(name, port);
+ }
+
+ pub fn parseIp4(text: []const u8, port: u16) Ip4Address.ParseError!IpAddress {
+ return .{ .ip4 = try Ip4Address.parse(text, port) };
+ }
+
+ /// This is a pure function but it cannot handle IPv6 addresses that have
+ /// scope ids ("%foo" at the end). To also handle those, `resolveIp6` must be
+ /// called instead.
+ pub fn parseIp6(text: []const u8, port: u16) Ip6Address.ParseError!IpAddress {
+ return .{ .ip6 = try Ip6Address.parse(text, port) };
+ }
+
+ /// This function requires an `Io` parameter because it must query the operating
+ /// system to convert interface name to index. For example, in
+ /// "fe80::e0e:76ff:fed4:cf22%eno1", "eno1" must be resolved to an index by
+ /// creating a socket and then using an `ioctl` syscall.
+ ///
+ /// For a pure function that cannot handle scopes, see `parse`.
+ pub fn resolve(io: Io, text: []const u8, port: u16) !IpAddress {
+ if (parseIp4(text, port)) |ip4| return ip4 else |err| switch (err) {
error.Overflow,
error.InvalidEnd,
error.InvalidCharacter,
error.Incomplete,
- error.InvalidIpv4Mapping,
+ error.NonCanonical,
=> {},
}
- return error.InvalidIpAddressFormat;
+ return resolveIp6(io, text, port);
}
- pub fn parseIp6(buffer: []const u8, port: u16) Ip6Address.ParseError!IpAddress {
- return .{ .ip6 = try Ip6Address.parse(buffer, port) };
- }
-
- pub fn parseIp4(buffer: []const u8, port: u16) Ip4Address.ParseError!IpAddress {
- return .{ .ip4 = try Ip4Address.parse(buffer, port) };
+ pub fn resolveIp6(io: Io, text: []const u8, port: u16) Ip6Address.ResolveError!IpAddress {
+ return .{ .ip6 = try Ip6Address.resolve(io, text, port) };
}
/// Returns the port in native endian.
@@ -74,6 +91,19 @@ pub const IpAddress = union(enum) {
}
}
+ /// Includes the optional scope ("%foo" at the end) in IPv6 addresses.
+ ///
+ /// See `format` for an alternative that omits scopes and does
+ /// not require an `Io` parameter.
+ pub fn formatResolved(a: IpAddress, io: Io, w: *Io.Writer) Ip6Address.FormatError!void {
+ switch (a) {
+ .ip4 => |x| return x.format(w),
+ .ip6 => |x| return x.formatResolved(io, w),
+ }
+ }
+
+ /// See `formatResolved` for an alternative that additionally prints the optional
+ /// scope at the end of IPv6 addresses and requires an `Io` parameter.
pub fn format(a: IpAddress, w: *Io.Writer) Io.Writer.Error!void {
switch (a) {
inline .ip4, .ip6 => |x| return x.format(w),
@@ -99,11 +129,12 @@ pub const IpAddress = union(enum) {
}
};
+/// An IPv4 address in binary memory layout.
pub const Ip4Address = struct {
bytes: [4]u8,
port: u16,
- pub fn localhost(port: u16) Ip4Address {
+ pub fn loopback(port: u16) Ip4Address {
return .{
.bytes = .{ 127, 0, 0, 1 },
.port = port,
@@ -162,21 +193,14 @@ pub const Ip4Address = struct {
}
};
+/// An IPv6 address in binary memory layout.
pub const Ip6Address = struct {
/// Native endian
port: u16,
/// Big endian
bytes: [16]u8,
- flowinfo: u32 = 0,
- scope_id: u32 = 0,
-
- pub const ParseError = error{
- Overflow,
- InvalidCharacter,
- InvalidEnd,
- InvalidIpv4Mapping,
- Incomplete,
- };
+ flow: u32 = 0,
+ interface: Interface = .none,
pub const Policy = struct {
addr: [16]u8,
@@ -186,192 +210,205 @@ pub const Ip6Address = struct {
label: u8,
};
- pub fn localhost(port: u16) Ip6Address {
+ pub fn loopback(port: u16) Ip6Address {
return .{
.bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
.port = port,
};
}
- pub fn parse(buffer: []const u8, port: u16) ParseError!Ip6Address {
- var result: Ip6Address = .{
- .port = port,
- .bytes = undefined,
+ /// An IPv6 address but with `Interface` as a name rather than index.
+ pub const Unresolved = struct {
+ /// Big endian
+ bytes: [16]u8,
+ interface_name: ?Interface.Name,
+
+ pub const Parsed = union(enum) {
+ success: Unresolved,
+ invalid_byte: usize,
+ unexpected_end,
};
- var ip_slice: *[16]u8 = &result.bytes;
- var tail: [16]u8 = undefined;
+ pub fn parse(buffer: []const u8) Parsed {
+ if (buffer.len < 2) return .unexpected_end;
+ var parts: [8]u16 = @splat(0);
+ var parts_i: usize = 0;
+ var i: usize = 0;
+ var digit_i: usize = 0;
+ const State = union(enum) { digit, colon, end };
+ state: switch (State.digit) {
+ .digit => c: switch (buffer[i]) {
+ 'a'...'f' => |c| {
+ const digit = c - 'a';
+ parts[parts_i] = parts[parts_i] * 16 + digit;
+ if (digit_i == 3) {
+ digit_i = 0;
+ parts_i += 1;
+ i += 1;
+ if (parts.len - parts_i == 0) continue :state .end;
+ continue :state .colon;
+ }
+ digit_i += 1;
+ if (buffer.len - i == 0) return .unexpected_end;
+ i += 1;
+ continue :c buffer[i];
+ },
+ 'A'...'F' => |c| continue :c c + ('a' - 'A'),
+ '0'...'9' => |c| continue :c c + ('a' - '0'),
+ ':' => @panic("TODO"),
+ else => return .{ .invalid_byte = i },
+ },
+ .colon => @panic("TODO"),
+ .end => @panic("TODO"),
+ }
+ }
+
+ pub const FromAddressError = Interface.NameError;
- var x: u16 = 0;
- var saw_any_digits = false;
- var index: u8 = 0;
- var scope_id = false;
- var abbrv = false;
- for (buffer, 0..) |c, i| {
- if (scope_id) {
- if (c >= '0' and c <= '9') {
- const digit = c - '0';
- {
- const ov = @mulWithOverflow(result.scope_id, 10);
- if (ov[1] != 0) return error.Overflow;
- result.scope_id = ov[0];
- }
- {
- const ov = @addWithOverflow(result.scope_id, digit);
- if (ov[1] != 0) return error.Overflow;
- result.scope_id = ov[0];
+ pub fn fromAddress(a: *const Ip6Address, io: Io) FromAddressError!Unresolved {
+ if (a.interface.isNone()) return .{
+ .bytes = a.bytes,
+ .interface_name = null,
+ };
+ return .{
+ .bytes = a.bytes,
+ .interface_name = try a.interface.name(io),
+ };
+ }
+
+ pub fn format(u: *const Unresolved, w: *Io.Writer) Io.Writer.Error!void {
+ const bytes = &u.bytes;
+ if (std.mem.eql(u8, bytes[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
+ try w.print("::ffff:{d}.{d}.{d}.{d}", .{ bytes[12], bytes[13], bytes[14], bytes[15] });
+ } else {
+ const parts: [8]u16 = .{
+ std.mem.readInt(u16, bytes[0..2], .big),
+ std.mem.readInt(u16, bytes[2..4], .big),
+ std.mem.readInt(u16, bytes[4..6], .big),
+ std.mem.readInt(u16, bytes[6..8], .big),
+ std.mem.readInt(u16, bytes[8..10], .big),
+ std.mem.readInt(u16, bytes[10..12], .big),
+ std.mem.readInt(u16, bytes[12..14], .big),
+ std.mem.readInt(u16, bytes[14..16], .big),
+ };
+
+ // Find the longest zero run
+ var longest_start: usize = 8;
+ var longest_len: usize = 0;
+ var current_start: usize = 0;
+ var current_len: usize = 0;
+
+ for (parts, 0..) |part, i| {
+ if (part == 0) {
+ if (current_len == 0) {
+ current_start = i;
+ }
+ current_len += 1;
+ if (current_len > longest_len) {
+ longest_start = current_start;
+ longest_len = current_len;
+ }
+ } else {
+ current_len = 0;
}
- } else {
- return error.InvalidCharacter;
}
- } else if (c == ':') {
- if (!saw_any_digits) {
- if (abbrv) return error.InvalidCharacter; // ':::'
- if (i != 0) abbrv = true;
- @memset(ip_slice[index..], 0);
- ip_slice = tail[0..];
- index = 0;
- continue;
- }
- if (index == 14) {
- return error.InvalidEnd;
- }
- ip_slice[index] = @as(u8, @truncate(x >> 8));
- index += 1;
- ip_slice[index] = @as(u8, @truncate(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;
+ // Only compress if the longest zero run is 2 or more
+ if (longest_len < 2) {
+ longest_start = 8;
+ longest_len = 0;
}
- const start_index = std.mem.lastIndexOfScalar(u8, buffer[0..i], ':').? + 1;
- const addr = (Ip4Address.parse(buffer[start_index..], 0) catch {
- return error.InvalidIpv4Mapping;
- }).bytes;
- ip_slice = result.bytes[0..];
- ip_slice[10] = 0xff;
- ip_slice[11] = 0xff;
-
- ip_slice[12] = addr[0];
- ip_slice[13] = addr[1];
- ip_slice[14] = addr[2];
- ip_slice[15] = addr[3];
- return result;
- } else {
- const digit = try std.fmt.charToDigit(c, 16);
- {
- const ov = @mulWithOverflow(x, 16);
- if (ov[1] != 0) return error.Overflow;
- x = ov[0];
- }
- {
- const ov = @addWithOverflow(x, digit);
- if (ov[1] != 0) return error.Overflow;
- x = ov[0];
+
+ try w.writeAll("[");
+ var i: usize = 0;
+ var abbrv = false;
+ while (i < parts.len) : (i += 1) {
+ if (i == longest_start) {
+ // Emit "::" for the longest zero run
+ if (!abbrv) {
+ try w.writeAll(if (i == 0) "::" else ":");
+ abbrv = true;
+ }
+ i += longest_len - 1; // Skip the compressed range
+ continue;
+ }
+ if (abbrv) {
+ abbrv = false;
+ }
+ try w.print("{x}", .{parts[i]});
+ if (i != parts.len - 1) {
+ try w.writeAll(":");
+ }
}
- saw_any_digits = true;
}
+ if (u.interface_name) |n| try w.print("%{s}", .{n.toSlice()});
}
+ };
- if (!saw_any_digits and !abbrv) {
- return error.Incomplete;
- }
- if (!abbrv and index < 14) {
- return error.Incomplete;
- }
+ pub const ParseError = error{
+ /// If this is returned, more detailed diagnostics can be obtained by
+ /// calling `Ip6Address.Parsed.init`.
+ ParseFailed,
+ /// If this is returned, the IPv6 address had a scope id on it ("%foo"
+ /// at the end) which requires calling `resolve`.
+ UnresolvedScope,
+ };
- if (index == 14) {
- ip_slice[14] = @as(u8, @truncate(x >> 8));
- ip_slice[15] = @as(u8, @truncate(x));
- return result;
- } else {
- ip_slice[index] = @as(u8, @truncate(x >> 8));
- index += 1;
- ip_slice[index] = @as(u8, @truncate(x));
- index += 1;
- @memcpy(result.bytes[16 - index ..][0..index], ip_slice[0..index]);
- return result;
+ /// This is a pure function but it cannot handle IPv6 addresses that have
+ /// scope ids ("%foo" at the end). To also handle those, `resolve` must be
+ /// called instead.
+ pub fn parse(buffer: []const u8, port: u16) ParseError!Ip6Address {
+ switch (Unresolved.parse(buffer)) {
+ .success => |p| return .{
+ .bytes = p.bytes,
+ .port = port,
+ .interface = if (p.interface_name != null) return error.UnresolvedScope else .none,
+ },
+ else => return error.ParseFailed,
}
+ return .{ .ip6 = try Ip6Address.parse(buffer, port) };
}
- pub fn format(a: Ip6Address, w: *Io.Writer) Io.Writer.Error!void {
- const bytes = &a.bytes;
- if (std.mem.eql(u8, bytes[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
- try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{
- bytes[12], bytes[13], bytes[14], bytes[15], a.port,
- });
- return;
- }
- const parts: [8]u16 = .{
- std.mem.readInt(u16, bytes[0..2], .big),
- std.mem.readInt(u16, bytes[2..4], .big),
- std.mem.readInt(u16, bytes[4..6], .big),
- std.mem.readInt(u16, bytes[6..8], .big),
- std.mem.readInt(u16, bytes[8..10], .big),
- std.mem.readInt(u16, bytes[10..12], .big),
- std.mem.readInt(u16, bytes[12..14], .big),
- std.mem.readInt(u16, bytes[14..16], .big),
+ pub const ResolveError = error{
+ /// If this is returned, more detailed diagnostics can be obtained by
+ /// calling the `Parsed.init` function.
+ ParseFailed,
+ } || Interface.Name.ResolveError;
+
+ /// This function requires an `Io` parameter because it must query the operating
+ /// system to convert interface name to index. For example, in
+ /// "fe80::e0e:76ff:fed4:cf22%eno1", "eno1" must be resolved to an index by
+ /// creating a socket and then using an `ioctl` syscall.
+ pub fn resolve(io: Io, buffer: []const u8, port: u16) ResolveError!Ip6Address {
+ return switch (Unresolved.parse(buffer)) {
+ .success => |p| return .{
+ .bytes = p.bytes,
+ .port = port,
+ .interface = if (p.interface_name) |n| try n.resolve(io) else .none,
+ },
+ else => return error.ParseFailed,
};
+ }
- // Find the longest zero run
- var longest_start: usize = 8;
- var longest_len: usize = 0;
- var current_start: usize = 0;
- var current_len: usize = 0;
+ pub const FormatError = Io.Writer.Error || Unresolved.FromAddressError;
- for (parts, 0..) |part, i| {
- if (part == 0) {
- if (current_len == 0) {
- current_start = i;
- }
- current_len += 1;
- if (current_len > longest_len) {
- longest_start = current_start;
- longest_len = current_len;
- }
- } else {
- current_len = 0;
- }
- }
-
- // Only compress if the longest zero run is 2 or more
- if (longest_len < 2) {
- longest_start = 8;
- longest_len = 0;
- }
+ /// Includes the optional scope ("%foo" at the end).
+ ///
+ /// See `format` for an alternative that omits scopes and does
+ /// not require an `Io` parameter.
+ pub fn formatResolved(a: Ip6Address, io: Io, w: *Io.Writer) FormatError!void {
+ const u: Unresolved = try .fromAddress(io);
+ try w.print("[{f}]:{d}", .{ u, a.port });
+ }
- try w.writeAll("[");
- var i: usize = 0;
- var abbrv = false;
- while (i < parts.len) : (i += 1) {
- if (i == longest_start) {
- // Emit "::" for the longest zero run
- if (!abbrv) {
- try w.writeAll(if (i == 0) "::" else ":");
- abbrv = true;
- }
- i += longest_len - 1; // Skip the compressed range
- continue;
- }
- if (abbrv) {
- abbrv = false;
- }
- try w.print("{x}", .{parts[i]});
- if (i != parts.len - 1) {
- try w.writeAll(":");
- }
- }
- try w.print("]:{d}", .{a.port});
+ /// See `formatResolved` for an alternative that additionally prints the optional
+ /// scope at the end of addresses and requires an `Io` parameter.
+ pub fn format(a: Ip6Address, w: *Io.Writer) Io.Writer.Error!void {
+ const u: Unresolved = .{
+ .bytes = a.bytes,
+ .interface_name = null,
+ };
+ try w.print("[{f}]:{d}", .{ u, a.port });
}
pub fn eql(a: Ip6Address, b: Ip6Address) bool {
@@ -471,11 +508,64 @@ pub const Ip6Address = struct {
};
};
+pub const Interface = struct {
+ /// Value 0 indicates `none`.
+ index: u32,
+
+ pub const none: Interface = .{ .index = 0 };
+
+ pub const Name = struct {
+ bytes: [max_len:0]u8,
+
+ pub const max_len = std.posix.IFNAMESIZE - 1;
+
+ pub fn toSlice(n: *const Name) []const u8 {
+ return std.mem.sliceTo(&n.bytes, 0);
+ }
+
+ pub fn fromSlice(bytes: []const u8) error{NameTooLong}!Name {
+ if (bytes.len > max_len) return error.NameTooLong;
+ var result: Name = undefined;
+ @memcpy(result.bytes[0..bytes.len], bytes);
+ result.bytes[bytes.len] = 0;
+ return result;
+ }
+
+ pub const ResolveError = error{
+ InterfaceNotFound,
+ AccessDenied,
+ SystemResources,
+ } || Io.UnexpectedError || Io.Cancelable;
+
+ /// Corresponds to "if_nametoindex" in libc.
+ pub fn resolve(n: []const u8, io: Io) ResolveError!Interface {
+ return io.vtable.netInterfaceNameResolve(io.userdata, n);
+ }
+ };
+
+ pub const NameError = Io.UnexpectedError || Io.Cancelable;
+
+ /// Asserts not `none`.
+ ///
+ /// Corresponds to "if_indextoname" in libc.
+ pub fn name(i: Interface, io: Io) NameError!Name {
+ assert(i.index != 0);
+ return io.vtable.netInterfaceName(io.userdata, i);
+ }
+
+ pub fn isNone(i: Interface) bool {
+ return i.index == 0;
+ }
+};
+
+/// An open socket connection with a network protocol that guarantees
+/// sequencing, delivery, and prevents repetition. Typically TCP or UNIX domain
+/// socket.
pub const Stream = struct {
- /// Underlying platform-defined type which may or may not be
- /// interchangeable with a file system file descriptor.
handle: Handle,
+ /// Underlying platform-defined type which may or may not be
+ /// interchangeable with a file system file descriptor.
pub const Handle = switch (native_os) {
.windows => std.windows.ws2_32.SOCKET,
else => std.posix.fd_t,
@@ -583,17 +673,6 @@ pub const Server = struct {
}
};
-pub const InterfaceIndexError = error{
- InterfaceNotFound,
- AccessDenied,
- SystemResources,
-} || Io.UnexpectedError || Io.Cancelable;
-
-/// Otherwise known as "if_nametoindex".
-pub fn interfaceIndex(io: Io, name: []const u8) InterfaceIndexError!u32 {
- return io.vtable.netInterfaceIndex(io.userdata, name);
-}
-
test {
_ = HostName;
}
lib/std/Io/Threaded.zig
@@ -135,7 +135,8 @@ pub fn io(pool: *Pool) Io {
else => netWritePosix,
},
.netClose = netClose,
- .netInterfaceIndex = netInterfaceIndex,
+ .netInterfaceNameResolve = netInterfaceNameResolve,
+ .netInterfaceName = netInterfaceName,
},
};
}
@@ -1123,16 +1124,11 @@ fn netClose(userdata: ?*anyopaque, stream: Io.net.Stream) void {
return net_stream.close();
}
-fn netInterfaceIndex(userdata: ?*anyopaque, name: []const u8) Io.net.InterfaceIndexError!u32 {
+fn netInterfaceNameResolve(userdata: ?*anyopaque, name: Io.net.Interface.Name) Io.net.Interface.Name.ResolveError!Io.net.Interface {
const pool: *Pool = @ptrCast(@alignCast(userdata));
try pool.checkCancel();
if (native_os == .linux) {
- if (name.len >= posix.IFNAMESIZE) return error.InterfaceNotFound;
- var ifr: posix.ifreq = undefined;
- @memcpy(ifr.ifrn.name[0..name.len], name);
- ifr.ifrn.name[name.len] = 0;
-
const rc = posix.system.socket(posix.AF.UNIX, posix.SOCK.DGRAM | posix.SOCK.CLOEXEC, 0);
const sock_fd: posix.fd_t = switch (posix.errno(rc)) {
.SUCCESS => @intCast(rc),
@@ -1145,10 +1141,15 @@ fn netInterfaceIndex(userdata: ?*anyopaque, name: []const u8) Io.net.InterfaceIn
};
defer posix.close(sock_fd);
+ var ifr: posix.ifreq = .{
+ .ifrn = .{ .name = @bitCast(name.bytes) },
+ .ifru = undefined,
+ };
+
while (true) {
try pool.checkCancel();
switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) {
- .SUCCESS => return @bitCast(ifr.ifru.ivalue),
+ .SUCCESS => return .{ .index = @bitCast(ifr.ifru.ivalue) },
.INVAL => |err| return badErrno(err), // Bad parameters.
.NOTTY => |err| return badErrno(err),
.NXIO => |err| return badErrno(err),
@@ -1162,28 +1163,39 @@ fn netInterfaceIndex(userdata: ?*anyopaque, name: []const u8) Io.net.InterfaceIn
}
}
- if (native_os.isDarwin()) {
- if (name.len >= posix.IFNAMESIZE) return error.InterfaceNotFound;
- var if_name: [posix.IFNAMESIZE:0]u8 = undefined;
- @memcpy(if_name[0..name.len], name);
- if_name[name.len] = 0;
- const if_slice = if_name[0..name.len :0];
- const index = std.c.if_nametoindex(if_slice);
+ if (native_os == .windows) {
+ const index = std.os.windows.ws2_32.if_nametoindex(&name.bytes);
if (index == 0) return error.InterfaceNotFound;
- return @bitCast(index);
+ return .{ .index = index };
}
- if (native_os == .windows) {
- if (name.len >= posix.IFNAMESIZE) return error.InterfaceNotFound;
- var interface_name: [posix.IFNAMESIZE:0]u8 = undefined;
- @memcpy(interface_name[0..name.len], name);
- interface_name[name.len] = 0;
- const index = std.os.windows.ws2_32.if_nametoindex(@as([*:0]const u8, &interface_name));
+ if (builtin.link_libc) {
+ const index = std.c.if_nametoindex(&name.bytes);
if (index == 0) return error.InterfaceNotFound;
- return index;
+ return .{ .index = @bitCast(index) };
+ }
+
+ @panic("unimplemented");
+}
+
+fn netInterfaceName(userdata: ?*anyopaque, interface: Io.net.Interface) Io.net.Interface.NameError!Io.net.Interface.Name {
+ const pool: *Pool = @ptrCast(@alignCast(userdata));
+ try pool.checkCancel();
+
+ if (native_os == .linux) {
+ _ = interface;
+ @panic("TODO");
+ }
+
+ if (native_os == .windows) {
+ @panic("TODO");
+ }
+
+ if (builtin.link_libc) {
+ @panic("TODO");
}
- @compileError("std.net.if_nametoindex unimplemented for this OS");
+ @panic("unimplemented");
}
const PosixAddress = extern union {
@@ -1231,8 +1243,8 @@ fn address6FromPosix(in6: *posix.sockaddr.in6) Io.net.Ip6Address {
return .{
.port = std.mem.bigToNative(u16, in6.port),
.bytes = in6.addr,
- .flowinfo = in6.flowinfo,
- .scope_id = in6.scope_id,
+ .flow = in6.flowinfo,
+ .interface = .{ .index = in6.scope_id },
};
}
@@ -1246,9 +1258,9 @@ fn address4ToPosix(a: Io.net.Ip4Address) posix.sockaddr.in {
fn address6ToPosix(a: Io.net.Ip6Address) posix.sockaddr.in6 {
return .{
.port = std.mem.nativeToBig(u16, a.port),
- .flowinfo = a.flowinfo,
+ .flowinfo = a.flow,
.addr = a.bytes,
- .scope_id = a.scope_id,
+ .scope_id = a.interface.index,
};
}