extern union Address [src]
Fields
any: posix.sockaddr
in: Ip4Address
in6: Ip6Address
un: if (has_unix_sockets) posix.sockaddr.un else void
Members
- eql (Function)
- format (Function)
- getOsSockLen (Function)
- getPort (Function)
- initIp4 (Function)
- initIp6 (Function)
- initPosix (Function)
- initUnix (Function)
- listen (Function)
- ListenError (Error Set)
- ListenOptions (struct)
- parseExpectingFamily (Function)
- parseIp (Function)
- parseIp4 (Function)
- parseIp6 (Function)
- resolveIp (Function)
- resolveIp6 (Function)
- setPort (Function)
Source
pub const Address = extern union {
any: posix.sockaddr,
in: Ip4Address,
in6: Ip6Address,
un: if (has_unix_sockets) posix.sockaddr.un else void,
/// Parse the given IP address string into an Address value.
/// 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) {
error.Overflow,
error.InvalidEnd,
error.InvalidCharacter,
error.Incomplete,
error.NonCanonical,
=> {},
}
if (parseIp6(name, port)) |ip6| return ip6 else |err| switch (err) {
error.Overflow,
error.InvalidEnd,
error.InvalidCharacter,
error.Incomplete,
error.InvalidIpv4Mapping,
=> {},
}
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,
error.NonCanonical,
=> {},
}
if (resolveIp6(name, port)) |ip6| return ip6 else |err| switch (err) {
error.Overflow,
error.InvalidEnd,
error.InvalidCharacter,
error.Incomplete,
error.InvalidIpv4Mapping,
=> {},
else => return err,
}
return error.InvalidIPAddressFormat;
}
pub fn parseExpectingFamily(name: []const u8, family: posix.sa_family_t, port: u16) !Address {
switch (family) {
posix.AF.INET => return parseIp4(name, port),
posix.AF.INET6 => return parseIp6(name, port),
posix.AF.UNSPEC => return parseIp(name, port),
else => unreachable,
}
}
pub fn parseIp6(buf: []const u8, port: u16) IPv6ParseError!Address {
return .{ .in6 = try Ip6Address.parse(buf, port) };
}
pub fn resolveIp6(buf: []const u8, port: u16) IPv6ResolveError!Address {
return .{ .in6 = try Ip6Address.resolve(buf, port) };
}
pub fn parseIp4(buf: []const u8, port: u16) IPv4ParseError!Address {
return .{ .in = try Ip4Address.parse(buf, port) };
}
pub fn initIp4(addr: [4]u8, port: u16) Address {
return .{ .in = Ip4Address.init(addr, port) };
}
pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {
return .{ .in6 = Ip6Address.init(addr, port, flowinfo, scope_id) };
}
pub fn initUnix(path: []const u8) !Address {
var sock_addr = posix.sockaddr.un{
.family = posix.AF.UNIX,
.path = undefined,
};
// Add 1 to ensure a terminating 0 is present in the path array for maximum portability.
if (path.len + 1 > sock_addr.path.len) return error.NameTooLong;
@memset(&sock_addr.path, 0);
@memcpy(sock_addr.path[0..path.len], path);
return .{ .un = sock_addr };
}
/// Returns the port in native endian.
/// Asserts that the address is ip4 or ip6.
pub fn getPort(self: Address) u16 {
return switch (self.any.family) {
posix.AF.INET => self.in.getPort(),
posix.AF.INET6 => self.in6.getPort(),
else => unreachable,
};
}
/// `port` is native-endian.
/// Asserts that the address is ip4 or ip6.
pub fn setPort(self: *Address, port: u16) void {
switch (self.any.family) {
posix.AF.INET => self.in.setPort(port),
posix.AF.INET6 => self.in6.setPort(port),
else => unreachable,
}
}
/// Asserts that `addr` is an IP address.
/// This function will read past the end of the pointer, with a size depending
/// on the address family.
pub fn initPosix(addr: *align(4) const posix.sockaddr) Address {
switch (addr.family) {
posix.AF.INET => return Address{ .in = Ip4Address{ .sa = @as(*const posix.sockaddr.in, @ptrCast(addr)).* } },
posix.AF.INET6 => return Address{ .in6 = Ip6Address{ .sa = @as(*const posix.sockaddr.in6, @ptrCast(addr)).* } },
else => unreachable,
}
}
pub fn format(
self: Address,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
out_stream: anytype,
) !void {
if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
switch (self.any.family) {
posix.AF.INET => try self.in.format(fmt, options, out_stream),
posix.AF.INET6 => try self.in6.format(fmt, options, out_stream),
posix.AF.UNIX => {
if (!has_unix_sockets) {
unreachable;
}
try std.fmt.format(out_stream, "{s}", .{std.mem.sliceTo(&self.un.path, 0)});
},
else => unreachable,
}
}
pub fn eql(a: Address, b: Address) bool {
const a_bytes = @as([*]const u8, @ptrCast(&a.any))[0..a.getOsSockLen()];
const b_bytes = @as([*]const u8, @ptrCast(&b.any))[0..b.getOsSockLen()];
return mem.eql(u8, a_bytes, b_bytes);
}
pub fn getOsSockLen(self: Address) posix.socklen_t {
switch (self.any.family) {
posix.AF.INET => return self.in.getOsSockLen(),
posix.AF.INET6 => return self.in6.getOsSockLen(),
posix.AF.UNIX => {
if (!has_unix_sockets) {
unreachable;
}
// Using the full length of the structure here is more portable than returning
// the number of bytes actually used by the currently stored path.
// This also is correct regardless if we are passing a socket address to the kernel
// (e.g. in bind, connect, sendto) since we ensure the path is 0 terminated in
// initUnix() or if we are receiving a socket address from the kernel and must
// provide the full buffer size (e.g. getsockname, getpeername, recvfrom, accept).
//
// To access the path, std.mem.sliceTo(&address.un.path, 0) should be used.
return @as(posix.socklen_t, @intCast(@sizeOf(posix.sockaddr.un)));
},
else => unreachable,
}
}
pub const ListenError = posix.SocketError || posix.BindError || posix.ListenError ||
posix.SetSockOptError || posix.GetSockNameError;
pub const ListenOptions = struct {
/// How many connections the kernel will accept on the application's behalf.
/// If more than this many connections pool in the kernel, clients will start
/// seeing "Connection refused".
kernel_backlog: u31 = 128,
/// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX.
/// Sets SO_REUSEADDR on Windows, which is roughly equivalent.
reuse_address: bool = false,
/// Deprecated. Does the same thing as reuse_address.
reuse_port: bool = false,
force_nonblocking: bool = false,
};
/// The returned `Server` has an open `stream`.
pub fn listen(address: Address, options: ListenOptions) ListenError!Server {
const nonblock: u32 = if (options.force_nonblocking) posix.SOCK.NONBLOCK else 0;
const sock_flags = posix.SOCK.STREAM | posix.SOCK.CLOEXEC | nonblock;
const proto: u32 = if (address.any.family == posix.AF.UNIX) 0 else posix.IPPROTO.TCP;
const sockfd = try posix.socket(address.any.family, sock_flags, proto);
var s: Server = .{
.listen_address = undefined,
.stream = .{ .handle = sockfd },
};
errdefer s.stream.close();
if (options.reuse_address or options.reuse_port) {
try posix.setsockopt(
sockfd,
posix.SOL.SOCKET,
posix.SO.REUSEADDR,
&mem.toBytes(@as(c_int, 1)),
);
if (@hasDecl(posix.SO, "REUSEPORT") and address.any.family != posix.AF.UNIX) {
try posix.setsockopt(
sockfd,
posix.SOL.SOCKET,
posix.SO.REUSEPORT,
&mem.toBytes(@as(c_int, 1)),
);
}
}
var socklen = address.getOsSockLen();
try posix.bind(sockfd, &address.any, socklen);
try posix.listen(sockfd, options.kernel_backlog);
try posix.getsockname(sockfd, &s.listen_address.any, &socklen);
return s;
}
}