const std = @import("std"); const linux = std.os.linux; pub const Address = extern union { any: linux.sockaddr, in: linux.sockaddr.in, in6: linux.sockaddr.in6, un: linux.sockaddr.un, pub fn initIp4(addr: [4]u8, port: u16) Address { return .{ .in = .{ .addr = @bitCast(addr), .port = std.mem.nativeToBig(u16, port), }, }; } pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address { return .{ .in6 = .{ .addr = addr, .port = std.mem.nativeToBig(u16, port), .flowinfo = flowinfo, .scope_id = scope_id, }, }; } pub fn initUnix(path: []const u8) !Address { var un: linux.sockaddr.un = .{ .path = @splat(0), }; // Ensure there is space for null terminator; already present by // initializing the path to zeroes above. if (path.len + 1 > un.path.len) return error.NameTooLong; @memcpy(&un.path[0..path.len], path); return .{ .un = un }; } pub fn getOsSockLen(self: Address) linux.socklen_t { return switch (self.any.family) { linux.AF.INET => @sizeOf(linux.sockaddr.in), linux.AF.INET6 => @sizeOf(linux.sockaddr.in6), // NOTE According to zig's 0.15.2 stdlib, this is technically always // correct especially since we ensure there is a space for a null // terminator in init. linux.AF.UNIX => @sizeOf(linux.sockaddr.un), else => unreachable, }; } pub fn format(self: *const Address, writer: *std.Io.Writer) std.Io.Writer.Error!void { switch (self.any.family) { linux.AF.INET => { const addr: std.Io.net.Ip4Address = .{ .bytes = @bitCast(self.in.addr), .port = std.mem.bigToNative(u16, self.in.port), }; try addr.format(writer); }, linux.AF.INET6 => { const addr: std.Io.net.Ip6Address.Unresolved = .{ .bytes = self.in6.addr, .interface_name = null, }; try addr.format(writer); }, linux.AF.UNIX => { try writer.writeAll(std.mem.sliceTo(&self.un.path, 0)); }, else => unreachable, } } };