diff --git a/lib/std/net.zig b/lib/std/net.zig index 71bab383fa85701ff33bb19059b0ff5d5365ae3d..514e48e0a718d4315fabfaf87498ef55fb6fabf9 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -14,8 +14,8 @@ const has_unix_sockets = @hasDecl(os, "sockaddr_un"); pub const Address = extern union { any: os.sockaddr, - in: os.sockaddr_in, - in6: os.sockaddr_in6, + in: Ip4Address, + in6: Ip6Address, un: if (has_unix_sockets) os.sockaddr_un else void, // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512 @@ -76,303 +76,24 @@ 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{ - .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 scope_id = false; - var abbrv = false; - for (buf) |c, i| { - if (scope_id) { - if (c >= '0' and c <= '9') { - const digit = c - '0'; - if (@mulWithOverflow(u32, result.in6.scope_id, 10, &result.in6.scope_id)) { - return error.Overflow; - } - if (@addWithOverflow(u32, result.in6.scope_id, digit, &result.in6.scope_id)) { - return error.Overflow; - } - } else { - return error.InvalidCharacter; - } - } 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; - } - - 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; - } + return Address{.in6 = try Ip6Address.parse(buf, port) }; } pub fn resolveIp6(buf: []const u8, port: u16) !Address { - // TODO: Unify the implementations of resolveIp6 and parseIp6. - 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: [os.IFNAMESIZE - 1]u8 = undefined; - var scope_id_index: usize = 0; - - for (buf) |c, i| { - if (scope_id) { - // 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 == '~')) - { - if (scope_id_index >= scope_id_value.len) { - return error.Overflow; - } - - 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; // ':::' - 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; - } - - 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]; - 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_str); - }; - } - - 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; - } + return Address{.in6 = try Ip6Address.resolve(buf, port) }; } pub fn parseIp4(buf: []const u8, port: u16) !Address { - var result = Address{ - .in = os.sockaddr_in{ - .port = mem.nativeToBig(u16, port), - .addr = undefined, - }, - }; - const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.in.addr)[0..]); - - var x: u8 = 0; - var index: u8 = 0; - var saw_any_digits = false; - for (buf) |c| { - if (c == '.') { - if (!saw_any_digits) { - return error.InvalidCharacter; - } - if (index == 3) { - return error.InvalidEnd; - } - out_ptr[index] = x; - index += 1; - x = 0; - saw_any_digits = false; - } else if (c >= '0' and c <= '9') { - saw_any_digits = true; - x = try std.math.mul(u8, x, 10); - x = try std.math.add(u8, x, c - '0'); - } else { - return error.InvalidCharacter; - } - } - if (index == 3 and saw_any_digits) { - out_ptr[index] = x; - return result; - } - - return error.Incomplete; + return Address {.in = try Ip4Address.parse(buf, port) }; } pub fn initIp4(addr: [4]u8, port: u16) Address { - return Address{ - .in = os.sockaddr_in{ - .port = mem.nativeToBig(u16, port), - .addr = @ptrCast(*align(1) const u32, &addr).*, - }, - }; + return Address{.in = Ip4Address.init(addr, port) }; } pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address { - return Address{ - .in6 = os.sockaddr_in6{ - .addr = addr, - .port = mem.nativeToBig(u16, port), - .flowinfo = flowinfo, - .scope_id = scope_id, - }, - }; + return Address{.in6 = Ip6Address.init(addr, port, flowinfo, scope_id) }; } pub fn initUnix(path: []const u8) !Address { @@ -393,23 +114,21 @@ pub const Address = extern union { /// Returns the port in native endian. /// Asserts that the address is ip4 or ip6. pub fn getPort(self: Address) u16 { - const big_endian_port = switch (self.any.family) { - os.AF_INET => self.in.port, - os.AF_INET6 => self.in6.port, + return switch (self.any.family) { + os.AF_INET => self.in.getPort(), + os.AF_INET6 => self.in6.getPort(), else => unreachable, }; - return mem.bigToNative(u16, big_endian_port); } /// `port` is native-endian. /// Asserts that the address is ip4 or ip6. pub fn setPort(self: *Address, port: u16) void { - const ptr = switch (self.any.family) { - os.AF_INET => &self.in.port, - os.AF_INET6 => &self.in6.port, + switch (self.any.family) { + os.AF_INET => self.in.setPort(port), + os.AF_INET6 => self.in6.setPort(port), else => unreachable, - }; - ptr.* = mem.nativeToBig(u16, port); + } } /// Asserts that `addr` is an IP address. @@ -417,8 +136,8 @@ pub const Address = extern union { /// on the address family. pub fn initPosix(addr: *align(4) const os.sockaddr) Address { switch (addr.family) { - os.AF_INET => return Address{ .in = @ptrCast(*const os.sockaddr_in, addr).* }, - os.AF_INET6 => return Address{ .in6 = @ptrCast(*const os.sockaddr_in6, addr).* }, + os.AF_INET => return Address{ .in = Ip4Address{ .sa = @ptrCast(*const os.sockaddr_in, addr).*} }, + os.AF_INET6 => return Address{ .in6 = Ip6Address{ .sa = @ptrCast(*const os.sockaddr_in6, addr).*} }, else => unreachable, } } @@ -430,58 +149,8 @@ pub const Address = extern union { out_stream: anytype, ) !void { switch (self.any.family) { - os.AF_INET => { - const port = mem.bigToNative(u16, self.in.port); - const bytes = @ptrCast(*const [4]u8, &self.in.addr); - try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{ - bytes[0], - bytes[1], - bytes[2], - bytes[3], - port, - }); - }, - os.AF_INET6 => { - const port = mem.bigToNative(u16, self.in6.port); - if (mem.eql(u8, self.in6.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { - try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{ - self.in6.addr[12], - self.in6.addr[13], - self.in6.addr[14], - self.in6.addr[15], - port, - }); - return; - } - const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr); - const native_endian_parts = switch (builtin.endian) { - .Big => big_endian_parts.*, - .Little => blk: { - var buf: [8]u16 = undefined; - for (big_endian_parts) |part, i| { - buf[i] = mem.bigToNative(u16, part); - } - break :blk buf; - }, - }; - try out_stream.writeAll("["); - var i: usize = 0; - var abbrv = false; - while (i < native_endian_parts.len) : (i += 1) { - if (native_endian_parts[i] == 0) { - if (!abbrv) { - try out_stream.writeAll(if (i == 0) "::" else ":"); - abbrv = true; - } - continue; - } - try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]}); - if (i != native_endian_parts.len - 1) { - try out_stream.writeAll(":"); - } - } - try std.fmt.format(out_stream, "]:{}", .{port}); - }, + os.AF_INET => try self.in.format(fmt, options, out_stream), + os.AF_INET6 => try self.in6.format(fmt, options, out_stream), os.AF_UNIX => { if (!has_unix_sockets) { unreachable; @@ -501,8 +170,8 @@ pub const Address = extern union { pub fn getOsSockLen(self: Address) os.socklen_t { switch (self.any.family) { - os.AF_INET => return @sizeOf(os.sockaddr_in), - os.AF_INET6 => return @sizeOf(os.sockaddr_in6), + os.AF_INET => return self.in.getOsSockLen(), + os.AF_INET6 => return self.in6.getOsSockLen(), os.AF_UNIX => { if (!has_unix_sockets) { unreachable; @@ -516,6 +185,420 @@ pub const Address = extern union { } }; +pub const Ip4Address = extern struct { + sa: os.sockaddr_in, + + pub fn parse(buf: []const u8, port: u16) !Ip4Address { + var result = Ip4Address{ + .sa = .{ + .port = mem.nativeToBig(u16, port), + .addr = undefined, + } + }; + const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.sa.addr)[0..]); + + var x: u8 = 0; + var index: u8 = 0; + var saw_any_digits = false; + for (buf) |c| { + if (c == '.') { + if (!saw_any_digits) { + return error.InvalidCharacter; + } + if (index == 3) { + return error.InvalidEnd; + } + out_ptr[index] = x; + index += 1; + x = 0; + saw_any_digits = false; + } else if (c >= '0' and c <= '9') { + saw_any_digits = true; + x = try std.math.mul(u8, x, 10); + x = try std.math.add(u8, x, c - '0'); + } else { + return error.InvalidCharacter; + } + } + if (index == 3 and saw_any_digits) { + out_ptr[index] = x; + return result; + } + + return error.Incomplete; + } + + pub fn resolveIp(name: []const u8, port: u16) !Ip4Address { + if (parse(name, port)) |ip4| return ip4 else |err| switch (err) { + error.Overflow, + error.InvalidEnd, + error.InvalidCharacter, + error.Incomplete, + => {}, + } + return error.InvalidIPAddressFormat; + } + + pub fn init(addr: [4]u8, port: u16) Ip4Address { + return Ip4Address { + .sa = os.sockaddr_in{ + .port = mem.nativeToBig(u16, port), + .addr = @ptrCast(*align(1) const u32, &addr).*, + }, + }; + } + + /// Returns the port in native endian. + /// Asserts that the address is ip4 or ip6. + pub fn getPort(self: Ip4Address) u16 { + return mem.bigToNative(u16, self.sa.port); + } + + /// `port` is native-endian. + /// Asserts that the address is ip4 or ip6. + pub fn setPort(self: *Ip4Address, port: u16) void { + self.sa.port = mem.nativeToBig(u16, port); + } + + pub fn format( + self: Ip4Address, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + out_stream: anytype, + ) !void { + const bytes = @ptrCast(*const [4]u8, &self.sa.addr); + try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{ + bytes[0], + bytes[1], + bytes[2], + bytes[3], + self.getPort(), + }); + } + + pub fn getOsSockLen(self: Ip4Address) os.socklen_t { + return @sizeOf(os.sockaddr_in); + } +}; + +pub const Ip6Address = extern struct { + sa: os.sockaddr_in6, + + /// 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 { + var result = Ip6Address{ + .sa = os.sockaddr_in6{ + .scope_id = 0, + .port = mem.nativeToBig(u16, port), + .flowinfo = 0, + .addr = undefined, + }, + }; + var ip_slice = result.sa.addr[0..]; + + var tail: [16]u8 = undefined; + + var x: u16 = 0; + var saw_any_digits = false; + var index: u8 = 0; + var scope_id = false; + var abbrv = false; + for (buf) |c, i| { + if (scope_id) { + if (c >= '0' and c <= '9') { + const digit = c - '0'; + if (@mulWithOverflow(u32, result.sa.scope_id, 10, &result.sa.scope_id)) { + return error.Overflow; + } + if (@addWithOverflow(u32, result.sa.scope_id, digit, &result.sa.scope_id)) { + return error.Overflow; + } + } else { + return error.InvalidCharacter; + } + } 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 = (Ip4Address.parse(buf[start_index..], 0) catch { + return error.InvalidIpv4Mapping; + }).sa.addr; + ip_slice = result.sa.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; + } + + 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.sa.addr[16 - index ..], ip_slice[0..index]); + return result; + } + } + + pub fn resolve(buf: []const u8, port: u16) !Ip6Address { + // TODO: Unify the implementations of resolveIp6 and parseIp6. + var result = Ip6Address{ + .sa = os.sockaddr_in6{ + .scope_id = 0, + .port = mem.nativeToBig(u16, port), + .flowinfo = 0, + .addr = undefined, + }, + }; + var ip_slice = result.sa.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: [os.IFNAMESIZE - 1]u8 = undefined; + var scope_id_index: usize = 0; + + for (buf) |c, i| { + if (scope_id) { + // 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 == '~')) + { + if (scope_id_index >= scope_id_value.len) { + return error.Overflow; + } + + 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; // ':::' + 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 = (Ip4Address.parse(buf[start_index..], 0) catch { + return error.InvalidIpv4Mapping; + }).sa.addr; + ip_slice = result.sa.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; + } + + 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]; + 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_str); + }; + } + + result.sa.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.sa.addr[16 - index ..], ip_slice[0..index]); + return result; + } + } + + pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address { + return Ip6Address{ + .sa = os.sockaddr_in6{ + .addr = addr, + .port = mem.nativeToBig(u16, port), + .flowinfo = flowinfo, + .scope_id = scope_id, + }, + }; + } + + /// Returns the port in native endian. + /// Asserts that the address is ip4 or ip6. + pub fn getPort(self: Ip6Address) u16 { + return mem.bigToNative(u16, self.sa.port); + } + + /// `port` is native-endian. + /// Asserts that the address is ip4 or ip6. + pub fn setPort(self: *Ip6Address, port: u16) void { + self.sa.port = mem.nativeToBig(u16, port); + } + + pub fn format( + self: Ip6Address, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + out_stream: anytype, + ) !void { + const port = mem.bigToNative(u16, self.sa.port); + if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { + try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{ + self.sa.addr[12], + self.sa.addr[13], + self.sa.addr[14], + self.sa.addr[15], + port, + }); + return; + } + const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.sa.addr); + const native_endian_parts = switch (builtin.endian) { + .Big => big_endian_parts.*, + .Little => blk: { + var buf: [8]u16 = undefined; + for (big_endian_parts) |part, i| { + buf[i] = mem.bigToNative(u16, part); + } + break :blk buf; + }, + }; + try out_stream.writeAll("["); + var i: usize = 0; + var abbrv = false; + while (i < native_endian_parts.len) : (i += 1) { + if (native_endian_parts[i] == 0) { + if (!abbrv) { + try out_stream.writeAll(if (i == 0) "::" else ":"); + abbrv = true; + } + continue; + } + try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]}); + if (i != native_endian_parts.len - 1) { + try out_stream.writeAll(":"); + } + } + try std.fmt.format(out_stream, "]:{}", .{port}); + } + + pub fn getOsSockLen(self: Ip6Address) os.socklen_t { + return @sizeOf(os.sockaddr_in6); + } +}; + + pub fn connectUnixSocket(path: []const u8) !fs.File { const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const sockfd = try os.socket( @@ -777,7 +860,7 @@ fn linuxLookupName( @memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr_in6)); var da6 = os.sockaddr_in6{ .family = os.AF_INET6, - .scope_id = addr.addr.in6.scope_id, + .scope_id = addr.addr.in6.sa.scope_id, .port = 65535, .flowinfo = 0, .addr = [1]u8{0} ** 16, @@ -795,7 +878,7 @@ fn linuxLookupName( var salen: os.socklen_t = undefined; var dalen: os.socklen_t = undefined; if (addr.addr.any.family == os.AF_INET6) { - mem.copy(u8, &da6.addr, &addr.addr.in6.addr); + mem.copy(u8, &da6.addr, &addr.addr.in6.sa.addr); da = @ptrCast(*os.sockaddr, &da6); dalen = @sizeOf(os.sockaddr_in6); sa = @ptrCast(*os.sockaddr, &sa6); @@ -803,8 +886,8 @@ fn linuxLookupName( } else { mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); - mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.addr); - da4.addr = addr.addr.in.addr; + mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.sa.addr); + da4.addr = addr.addr.in.sa.addr; da = @ptrCast(*os.sockaddr, &da4); dalen = @sizeOf(os.sockaddr_in); sa = @ptrCast(*os.sockaddr, &sa4);