Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

IpAddress

net.IpAddress
pub const IpAddress = union(enum)

File

lib/std/Io/net.zig:57

Code

pub const IpAddress = union(enum) {
    ip4: Ip4Address,
    ip6: Ip6Address,

    pub const Family = @typeInfo(IpAddress).@"union".tag_type.?;

    pub const ParseLiteralError = error{ InvalidAddress, InvalidPort };

    /// Parse an IP address which may include a port.
    ///
    /// For IPv4, this is written `address:port`.
    ///
    /// For IPv6, RFC 3986 defines this as an "IP literal", and the port is
    /// differentiated from the address by surrounding the address part in
    /// brackets "[addr]:port". Even if the port is not given, the brackets are
    /// mandatory.
    pub fn parseLiteral(text: []const u8) ParseLiteralError!IpAddress {
        if (text.len == 0) return error.InvalidAddress;
        if (text[0] == '[') {
            const addr_end = std.mem.findScalar(u8, text, ']') orelse
                return error.InvalidAddress;
            const addr_text = text[1..addr_end];
            const port: u16 = p: {
                if (addr_end == text.len - 1) break :p 0;
                if (text[addr_end + 1] != ':') return error.InvalidAddress;
                break :p std.fmt.parseInt(u16, text[addr_end + 2 ..], 10) catch return error.InvalidPort;
            };
            return parseIp6(addr_text, port) catch error.InvalidAddress;
        }
        if (std.mem.findScalar(u8, text, ':')) |i| {
            const addr = Ip4Address.parse(text[0..i], 0) catch return error.InvalidAddress;
            return .{ .ip4 = .{
                .bytes = addr.bytes,
                .port = std.fmt.parseInt(u16, text[i + 1 ..], 10) catch return error.InvalidPort,
            } };
        }
        return parseIp4(text, 0) catch error.InvalidAddress;
    }

    /// 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(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.NonCanonical,
            => {},
        }

        return parseIp6(text, 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.NonCanonical,
            => {},
        }

        return resolveIp6(io, text, 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.
    pub fn getPort(a: IpAddress) u16 {
        return switch (a) {
            inline .ip4, .ip6 => |x| x.port,
        };
    }

    /// `port` is native-endian.
    pub fn setPort(a: *IpAddress, port: u16) void {
        switch (a.*) {
            .ip4 => a.ip4.port = port,
            .ip6 => a.ip6.port = port,
        }
    }

    /// Converts from an IPv4-mapped IPv6 address, or returns the IPv6 address directly.
    pub fn fromIp6(ip6: Ip6Address) IpAddress {
        return if (Ip4Address.fromIp6(ip6)) |ip4| .{ .ip4 = ip4 } else .{ .ip6 = ip6 };
    }

    /// 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),
        }
    }

    pub fn eql(a: *const IpAddress, b: *const IpAddress) bool {
        return switch (a.*) {
            .ip4 => |a_ip4| switch (b.*) {
                .ip4 => |b_ip4| a_ip4.eql(b_ip4),
                else => false,
            },
            .ip6 => |a_ip6| switch (b.*) {
                .ip6 => |b_ip6| a_ip6.eql(b_ip6),
                else => false,
            },
        };
    }

    pub const ListenError = error{
        /// The address is already taken. Can occur when bound port is 0 but
        /// all ephemeral ports are already in use.
        AddressInUse,
        /// A nonexistent interface was requested or the requested address was not local.
        AddressUnavailable,
        /// The local network interface used to reach the destination is offline.
        NetworkDown,
        /// Insufficient memory or other resource internal to the operating system.
        SystemResources,
        /// Per-process limit on the number of open file descriptors has been reached.
        ProcessFdQuotaExceeded,
        /// System-wide limit on the total number of open files has been reached.
        SystemFdQuotaExceeded,
        /// The requested address family (IPv4 or IPv6) is not supported by the operating system.
        AddressFamilyUnsupported,
        ProtocolUnsupportedBySystem,
        ProtocolUnsupportedByAddressFamily,
        SocketModeUnsupported,
        /// One of the `ListenOptions` is not supported by the Io
        /// implementation.
        OptionUnsupported,
    } || Io.UnexpectedError || Io.Cancelable;

    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 = default_kernel_backlog,
        /// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX.
        /// Sets SO_REUSEADDR on Windows, which is roughly equivalent.
        reuse_address: bool = false,
        /// Only connection-oriented modes may be used here, which includes:
        /// * `Socket.Mode.stream`
        /// * `Socket.Mode.seqpacket`
        mode: Socket.Mode = .stream,
        /// Only connection-oriented protocols may be used here, which includes:
        /// * `Protocol.tcp`
        /// * `Protocol.tp`
        /// * `Protocol.dccp`
        /// * `Protocol.sctp`
        protocol: Protocol = .tcp,
    };

    /// Waits for a TCP connection. When using this API, `bind` does not need
    /// to be called. The returned `Server` has an open `stream`.
    pub fn listen(address: *const IpAddress, io: Io, options: ListenOptions) ListenError!Server {
        return .{
            .socket = try io.vtable.netListenIp(io.userdata, address, options),
            .options = if (Server.AcceptOptions != void) .{
                .mode = options.mode,
                .protocol = options.protocol,
            },
        };
    }

    pub const BindError = error{
        /// The address is already taken. Can occur when bound port is 0 but
        /// all ephemeral ports are already in use.
        AddressInUse,
        /// A nonexistent interface was requested or the requested address was not local.
        AddressUnavailable,
        /// The address is not valid for the address family of socket.
        AddressFamilyUnsupported,
        /// Insufficient memory or other resource internal to the operating system.
        SystemResources,
        /// The local network interface used to reach the destination is offline.
        NetworkDown,
        ProtocolUnsupportedBySystem,
        ProtocolUnsupportedByAddressFamily,
        /// Per-process limit on the number of open file descriptors has been reached.
        ProcessFdQuotaExceeded,
        /// System-wide limit on the total number of open files has been reached.
        SystemFdQuotaExceeded,
        SocketModeUnsupported,
        /// One of the `BindOptions` is not supported by the Io
        /// implementation.
        OptionUnsupported,
    } || Io.UnexpectedError || Io.Cancelable;

    pub const BindOptions = struct {
        /// The socket is restricted to sending and receiving IPv6 packets only.
        /// In this case, an IPv4 and an IPv6 application can bind to a single port
        /// at the same time.
        ///
        /// The default is determined by system configuration.
        ip6_only: ?bool = null,
        /// Allow the socket to send datagrams to broadcast addresses.
        /// When not enabled any attempt to send datagrams to a broadcast address
        /// will fail with `error.AccessDenied`
        allow_broadcast: bool = false,
        mode: Socket.Mode,
        protocol: ?Protocol = null,
    };

    /// Associates an address with a `Socket` which can be used to receive UDP
    /// packets and other kinds of non-streaming messages. See `listen` for a
    /// streaming alternative.
    ///
    /// One bound `Socket` can be used to receive messages from multiple
    /// different addresses.
    pub fn bind(address: *const IpAddress, io: Io, options: BindOptions) BindError!Socket {
        return io.vtable.netBindIp(io.userdata, address, options);
    }

    pub const ConnectError = error{
        AddressUnavailable,
        AddressFamilyUnsupported,
        /// Insufficient memory or other resource internal to the operating system.
        SystemResources,
        ConnectionPending,
        ConnectionRefused,
        ConnectionResetByPeer,
        HostUnreachable,
        NetworkUnreachable,
        Timeout,
        /// One of the `ConnectOptions` is not supported by the Io
        /// implementation.
        OptionUnsupported,
        /// Per-process limit on the number of open file descriptors has been reached.
        ProcessFdQuotaExceeded,
        /// System-wide limit on the total number of open files has been reached.
        SystemFdQuotaExceeded,
        ProtocolUnsupportedBySystem,
        ProtocolUnsupportedByAddressFamily,
        SocketModeUnsupported,
        /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
        /// the connection request failed because of a local firewall rule.
        AccessDenied,
        /// Non-blocking was requested and the operation cannot return immediately.
        WouldBlock,
        NetworkDown,
    } || Io.Timeout.Error || Io.UnexpectedError || Io.Cancelable;

    pub const ConnectOptions = struct {
        mode: Socket.Mode,
        protocol: ?Protocol = null,
        timeout: Io.Timeout = .none,
    };

    /// Initiates a connection-oriented network stream.
    pub fn connect(address: *const IpAddress, io: Io, options: ConnectOptions) ConnectError!Stream {
        return .{ .socket = try io.vtable.netConnectIp(io.userdata, address, options) };
    }
}