diff options
| author | Luna <git@l4.pm> | 2020-03-29 16:28:53 -0300 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-06-02 14:56:05 -0400 |
| commit | b8163031220d94a78c378ff90317882d10dfe067 (patch) | |
| tree | 2e7bcc97792b476a1f3e11f1a6e5a9f65299d823 /lib/std/net.zig | |
| parent | bae0c9b554105092297330c8beec977fe3468da6 (diff) | |
| download | zig-b8163031220d94a78c378ff90317882d10dfe067.tar.gz zig-b8163031220d94a78c378ff90317882d10dfe067.zip | |
Add basics of resolveIp6
Instead of streaming the scope id digits to an u32, we keep a [32]u8 in
the stack and fill it up with the characters we get for scope id.
Diffstat (limited to 'lib/std/net.zig')
| -rw-r--r-- | lib/std/net.zig | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/lib/std/net.zig b/lib/std/net.zig index ea133b1e67..f757585f34 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -21,6 +21,9 @@ pub const Address = extern union { // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512 //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); + /// Parse the given IP address string into an Address value. + /// It is recommended to use Address.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, @@ -42,6 +45,27 @@ pub const Address = extern union { 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, + => {}, + } + + if (resolveIp6(name, port)) |ip6| return ip6 else |err| switch (err) { + error.Overflow, + error.InvalidEnd, + error.InvalidCharacter, + error.Incomplete, + error.InvalidIpv4Mapping, + => {}, + } + + return error.InvalidIPAddressFormat; + } + pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address { switch (family) { os.AF_INET => return parseIp4(name, port), @@ -157,6 +181,117 @@ pub const Address = extern union { } } + pub fn resolveIp6(buf: []const u8, port: u16) !Address { + // FIXME: implement if_nametoindex + // FIXME: this is a very bad implementation, since it's only a copy + // of parseIp6 with alphanumerical scope id support + var result = Address{ + .in6 = os.sockaddr_in6{ + .scope_id = 0, + .port = mem.nativeToBig(u16, port), + .flowinfo = 0, + .addr = undefined, + }, + }; + var ip_slice = result.in6.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: [32]u8 = undefined; + var scope_id_index: usize = 0; + + for (buf) |c, i| { + if (scope_id) { + scope_id_value[scope_id_index] = c; + scope_id_index += 1; + } else if (c == ':') { + if (!saw_any_digits) { + if (abbrv) return error.InvalidCharacter; // ':::' + if (i != 0) abbrv = true; + mem.set(u8, ip_slice[index..], 0); + ip_slice = tail[0..]; + index = 0; + continue; + } + if (index == 14) { + return error.InvalidEnd; + } + ip_slice[index] = @truncate(u8, x >> 8); + index += 1; + ip_slice[index] = @truncate(u8, 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 = (parseIp4(buf[start_index..], 0) catch { + return error.InvalidIpv4Mapping; + }).in.addr; + ip_slice = result.in6.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); + if (@mulWithOverflow(u16, x, 16, &x)) { + return error.Overflow; + } + if (@addWithOverflow(u16, x, digit, &x)) { + return error.Overflow; + } + saw_any_digits = true; + } + } + + if (!saw_any_digits and !abbrv) { + return error.Incomplete; + } + + const resolved_scope_id = std.fmt.parseInt(u32, scope_id_value, 10) catch |err| blk: { + if (err != err.InvalidCharacter) return err; + break :blk if_nametoindex(scope_id_value); + } + + result.in6.scope_id = resolved_scope_id; + + if (index == 14) { + ip_slice[14] = @truncate(u8, x >> 8); + ip_slice[15] = @truncate(u8, x); + return result; + } else { + ip_slice[index] = @truncate(u8, x >> 8); + index += 1; + ip_slice[index] = @truncate(u8, x); + index += 1; + mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]); + return result; + } + } + pub fn parseIp4(buf: []const u8, port: u16) !Address { var result = Address{ .in = os.sockaddr_in{ |
