const std = @import("std"); const ArgError = error{ NotEnoughArgs, BadDomain, InvalidAddressVer, InterfaceRequired, InvalidInterface, EnvVarNotSet, }; pub const IP_VER_ENUM = enum(u3) { IPv4 = 4, IPv6 = 6, }; pub const IP = ?std.net.Address; pub const IPInfo = struct { version: IP_VER_ENUM, interface: ?[:0]const u8, }; pub const Domain = struct { name: [:0]const u8, labels: [5][]const u8, }; pub fn getenv(key: [*:0]const u8) ![*:0]const u8 { return (std.c.getenv(key) orelse ArgError.EnvVarNotSet); } pub fn check_perms() !void { var f = try std.fs.openFileAbsoluteZ(try getenv("OLD_HOSTS_PATH"), .{ .mode = .write_only }); f.close(); } pub fn get_input() !struct { Domain, IPInfo, } { var args = std.process.args(); _ = args.next(); // consume calling binary arg const domain_str = args.next() orelse return ArgError.NotEnoughArgs; const domain = try check_domain(domain_str); const ip_ver_str = args.next() orelse return ArgError.NotEnoughArgs; const ip_ver = std.meta.intToEnum(IP_VER_ENUM, std.fmt.parseInt(u3, ip_ver_str, 10) catch return ArgError.InvalidAddressVer) catch return ArgError.InvalidAddressVer; var iface: ?[:0]const u8 = null; if (ip_ver == IP_VER_ENUM.IPv6) { iface = args.next() orelse return ArgError.InterfaceRequired; if (std.c.if_nametoindex(iface.?) == 0) { return ArgError.InvalidInterface; } } return .{ domain, IPInfo{ .version = ip_ver, .interface = iface } }; } fn check_domain(domain_str: [:0]const u8) !Domain { var domain = Domain{ .name = domain_str, .labels = [_][]const u8{&[_]u8{}} ** 5, }; var labels = std.mem.splitScalar(u8, domain_str, '.'); var last: []const u8 = ""; var i: usize = 0; while (labels.next()) |segment| { if (i >= 5) { return ArgError.BadDomain; } last = segment; domain.labels[i] = segment; i += 1; } if (domain.labels.len <= 1 or !std.mem.eql(u8, last, "local")) { return ArgError.BadDomain; } return domain; }