extern structstd.net.Ip6Address[src]

Fields

Functions

Functionparse[src]

pub fn parse(buf: []const u8, port: u16) IPv6ParseError!Ip6Address

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.

Parameters

buf: []const u8
port: u16

Source Code

Source code
pub fn parse(buf: []const u8, port: u16) IPv6ParseError!Ip6Address {
    var result = Ip6Address{
        .sa = posix.sockaddr.in6{
            .scope_id = 0,
            .port = mem.nativeToBig(u16, port),
            .flowinfo = 0,
            .addr = undefined,
        },
    };
    var ip_slice: *[16]u8 = 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, 0..) |c, i| {
        if (scope_id) {
            if (c >= '0' and c <= '9') {
                const digit = c - '0';
                {
                    const ov = @mulWithOverflow(result.sa.scope_id, 10);
                    if (ov[1] != 0) return error.Overflow;
                    result.sa.scope_id = ov[0];
                }
                {
                    const ov = @addWithOverflow(result.sa.scope_id, digit);
                    if (ov[1] != 0) return error.Overflow;
                    result.sa.scope_id = ov[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;
            }
            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);
            {
                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];
            }
            saw_any_digits = true;
        }
    }

    if (!saw_any_digits and !abbrv) {
        return error.Incomplete;
    }
    if (!abbrv and index < 14) {
        return error.Incomplete;
    }

    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.sa.addr[16 - index ..][0..index], ip_slice[0..index]);
        return result;
    }
}

Functionresolve[src]

pub fn resolve(buf: []const u8, port: u16) IPv6ResolveError!Ip6Address

Parameters

buf: []const u8
port: u16

Source Code

Source code
pub fn resolve(buf: []const u8, port: u16) IPv6ResolveError!Ip6Address {
    // TODO: Unify the implementations of resolveIp6 and parseIp6.
    var result = Ip6Address{
        .sa = posix.sockaddr.in6{
            .scope_id = 0,
            .port = mem.nativeToBig(u16, port),
            .flowinfo = 0,
            .addr = undefined,
        },
    };
    var ip_slice: *[16]u8 = 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: [posix.IFNAMESIZE - 1]u8 = undefined;
    var scope_id_index: usize = 0;

    for (buf, 0..) |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;
                @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;
            }
            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);
            {
                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];
            }
            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] = @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.sa.addr[16 - index ..][0..index], ip_slice[0..index]);
        return result;
    }
}

Functioninit[src]

pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address

Parameters

addr: [16]u8
port: u16
flowinfo: u32
scope_id: u32

Source Code

Source code
pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address {
    return Ip6Address{
        .sa = posix.sockaddr.in6{
            .addr = addr,
            .port = mem.nativeToBig(u16, port),
            .flowinfo = flowinfo,
            .scope_id = scope_id,
        },
    };
}

FunctiongetPort[src]

pub fn getPort(self: Ip6Address) u16

Returns the port in native endian. Asserts that the address is ip4 or ip6.

Parameters

Source Code

Source code
pub fn getPort(self: Ip6Address) u16 {
    return mem.bigToNative(u16, self.sa.port);
}

FunctionsetPort[src]

pub fn setPort(self: *Ip6Address, port: u16) void

port is native-endian. Asserts that the address is ip4 or ip6.

Parameters

self: *Ip6Address
port: u16

Source Code

Source code
pub fn setPort(self: *Ip6Address, port: u16) void {
    self.sa.port = mem.nativeToBig(u16, port);
}

Functionformat[src]

pub fn format( self: Ip6Address, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype, ) !void

Parameters

fmt: []const u8

Source Code

Source code
pub fn format(
    self: Ip6Address,
    comptime fmt: []const u8,
    options: std.fmt.FormatOptions,
    out_stream: anytype,
) !void {
    if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
    _ = options;
    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 = @as(*align(1) const [8]u16, @ptrCast(&self.sa.addr));
    const native_endian_parts = switch (native_endian) {
        .big => big_endian_parts.*,
        .little => blk: {
            var buf: [8]u16 = undefined;
            for (big_endian_parts, 0..) |part, i| {
                buf[i] = mem.bigToNative(u16, part);
            }
            break :blk buf;
        },
    };

    // 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 (native_endian_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;
    }

    try out_stream.writeAll("[");
    var i: usize = 0;
    var abbrv = false;
    while (i < native_endian_parts.len) : (i += 1) {
        if (i == longest_start) {
            // Emit "::" for the longest zero run
            if (!abbrv) {
                try out_stream.writeAll(if (i == 0) "::" else ":");
                abbrv = true;
            }
            i += longest_len - 1; // Skip the compressed range
            continue;
        }
        if (abbrv) {
            abbrv = false;
        }
        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});
}

FunctiongetOsSockLen[src]

pub fn getOsSockLen(self: Ip6Address) posix.socklen_t

Parameters

Source Code

Source code
pub fn getOsSockLen(self: Ip6Address) posix.socklen_t {
    _ = self;
    return @sizeOf(posix.sockaddr.in6);
}

Source Code

Source code
pub const Ip6Address = extern struct {
    sa: posix.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) IPv6ParseError!Ip6Address {
        var result = Ip6Address{
            .sa = posix.sockaddr.in6{
                .scope_id = 0,
                .port = mem.nativeToBig(u16, port),
                .flowinfo = 0,
                .addr = undefined,
            },
        };
        var ip_slice: *[16]u8 = 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, 0..) |c, i| {
            if (scope_id) {
                if (c >= '0' and c <= '9') {
                    const digit = c - '0';
                    {
                        const ov = @mulWithOverflow(result.sa.scope_id, 10);
                        if (ov[1] != 0) return error.Overflow;
                        result.sa.scope_id = ov[0];
                    }
                    {
                        const ov = @addWithOverflow(result.sa.scope_id, digit);
                        if (ov[1] != 0) return error.Overflow;
                        result.sa.scope_id = ov[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;
                }
                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);
                {
                    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];
                }
                saw_any_digits = true;
            }
        }

        if (!saw_any_digits and !abbrv) {
            return error.Incomplete;
        }
        if (!abbrv and index < 14) {
            return error.Incomplete;
        }

        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.sa.addr[16 - index ..][0..index], ip_slice[0..index]);
            return result;
        }
    }

    pub fn resolve(buf: []const u8, port: u16) IPv6ResolveError!Ip6Address {
        // TODO: Unify the implementations of resolveIp6 and parseIp6.
        var result = Ip6Address{
            .sa = posix.sockaddr.in6{
                .scope_id = 0,
                .port = mem.nativeToBig(u16, port),
                .flowinfo = 0,
                .addr = undefined,
            },
        };
        var ip_slice: *[16]u8 = 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: [posix.IFNAMESIZE - 1]u8 = undefined;
        var scope_id_index: usize = 0;

        for (buf, 0..) |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;
                    @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;
                }
                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);
                {
                    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];
                }
                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] = @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.sa.addr[16 - index ..][0..index], ip_slice[0..index]);
            return result;
        }
    }

    pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address {
        return Ip6Address{
            .sa = posix.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 {
        if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
        _ = options;
        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 = @as(*align(1) const [8]u16, @ptrCast(&self.sa.addr));
        const native_endian_parts = switch (native_endian) {
            .big => big_endian_parts.*,
            .little => blk: {
                var buf: [8]u16 = undefined;
                for (big_endian_parts, 0..) |part, i| {
                    buf[i] = mem.bigToNative(u16, part);
                }
                break :blk buf;
            },
        };

        // 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 (native_endian_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;
        }

        try out_stream.writeAll("[");
        var i: usize = 0;
        var abbrv = false;
        while (i < native_endian_parts.len) : (i += 1) {
            if (i == longest_start) {
                // Emit "::" for the longest zero run
                if (!abbrv) {
                    try out_stream.writeAll(if (i == 0) "::" else ":");
                    abbrv = true;
                }
                i += longest_len - 1; // Skip the compressed range
                continue;
            }
            if (abbrv) {
                abbrv = false;
            }
            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) posix.socklen_t {
        _ = self;
        return @sizeOf(posix.sockaddr.in6);
    }
}