extern struct Ip6Address [src]

Fields

sa: posix.sockaddr.in6

Members

Source

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); } }