feature. See also
. The project being documented here (as the example) is the Zig library itself.
Threaded.netLookupFallible
fn netLookupFallible(
t: *Threaded,
host_name: HostName,
resolved: *Io.Queue(HostName.LookupResult),
options: HostName.LookupOptions,
) (net.HostName.LookupError || Io.QueueClosedError)!void
File
Code
fn netLookupFallible(
t: *Threaded,
host_name: HostName,
resolved: *Io.Queue(HostName.LookupResult),
options: HostName.LookupOptions,
) (net.HostName.LookupError || Io.QueueClosedError)!void {
if (!have_networking) return error.NetworkDown;
const t_io = t.io();
const name = host_name.bytes;
assert(name.len <= HostName.max_len);
// However, musl's POSIX-compliant getaddrinfo is not, so we bypass it.
const is_glibc = builtin.link_libc and builtin.target.isGnuLibC();
if (is_glibc) {
}
const non_glibc_linux = native_os == .linux and !is_glibc;
if (non_glibc_linux or is_windows) {
if (IpAddress.parseIp6(name, options.port)) |addr| {
if (options.family == .ip4) return error.UnknownHostName;
if (copyCanon(options.canonical_name_buffer, name)) |canon| {
try resolved.putAll(t_io, &.{
.{ .address = addr },
.{ .canonical_name = canon },
});
} else {
try resolved.putOne(t_io, .{ .address = addr });
}
return;
} else |_| {}
if (IpAddress.parseIp4(name, options.port)) |addr| {
if (options.family == .ip6) return error.UnknownHostName;
if (copyCanon(options.canonical_name_buffer, name)) |canon| {
try resolved.putAll(t_io, &.{
.{ .address = addr },
.{ .canonical_name = canon },
});
} else {
try resolved.putOne(t_io, .{ .address = addr });
}
return;
} else |_| {}
if (t.lookupHosts(host_name, resolved, options)) return else |err| switch (err) {
error.UnknownHostName => {},
else => |e| return e,
}
// Name resolution APIs and libraries SHOULD recognize
// localhost names as special and SHOULD always return the IP
// loopback address for address queries and negative responses
// for all other query types.
// Check for equal to "localhost(.)" or ends in ".localhost(.)"
const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost";
if (std.mem.endsWith(u8, name, localhost) and
(name.len == localhost.len or name[name.len - localhost.len - 1] == '.'))
{
var results_buffer: [3]HostName.LookupResult = undefined;
var results_index: usize = 0;
if (options.family != .ip4) {
results_buffer[results_index] = .{ .address = .{ .ip6 = .loopback(options.port) } };
results_index += 1;
}
if (options.family != .ip6) {
results_buffer[results_index] = .{ .address = .{ .ip4 = .loopback(options.port) } };
results_index += 1;
}
if (options.canonical_name_buffer) |buf| {
const canon_name = "localhost";
const canon_name_dest = buf[0..canon_name.len];
canon_name_dest.* = canon_name.*;
results_buffer[results_index] = .{ .canonical_name = .{ .bytes = canon_name_dest } };
results_index += 1;
}
try resolved.putAll(t_io, results_buffer[0..results_index]);
return;
}
if (native_os == .linux) return t.lookupDnsSearch(host_name, resolved, options);
comptime assert(is_windows);
var DnsQueryEx = t.dl.DnsQueryEx.load(.acquire);
var DnsFree = t.dl.DnsFree.load(.acquire);
if (DnsQueryEx == null or
DnsFree == null)
{
const dnsapi_dll = t.dl.dnsapi_dll.load(.acquire) orelse dnsapi_dll: {
try Thread.checkCancel();
var dnsapi_dll: *anyopaque = undefined;
switch (windows.ntdll.LdrLoadDll(null, null, &.init(
&.{ 'd', 'n', 's', 'a', 'p', 'i', '.', 'd', 'l', 'l' },
), &dnsapi_dll)) {
.SUCCESS => {},
.DLL_NOT_FOUND => return error.Unexpected,
else => |status| return windows.unexpectedStatus(status),
}
const handle = t.dl.dnsapi_dll.cmpxchgStrong(null, dnsapi_dll, .release, .monotonic) orelse
break :dnsapi_dll dnsapi_dll;
switch (windows.ntdll.LdrUnloadDll(dnsapi_dll)) {
.SUCCESS => break :dnsapi_dll handle.?,
else => |status| return windows.unexpectedStatus(status),
}
};
switch (windows.ntdll.LdrGetProcedureAddress(dnsapi_dll, &.init(
&.{ 'D', 'n', 's', 'Q', 'u', 'e', 'r', 'y', 'E', 'x' },
), 0, @ptrCast(&DnsQueryEx))) {
.SUCCESS => t.dl.DnsQueryEx.store(DnsQueryEx, .release),
else => |status| return windows.unexpectedStatus(status),
}
// &.{ 'D', 'n', 's', 'C', 'a', 'n', 'c', 'e', 'l', 'Q', 'u', 'e', 'r', 'y' },
//), 0, @ptrCast(&DnsCancelQuery))) {
// .SUCCESS => t.dl.DnsCancelQuery.store(DnsCancelQuery, .release),
// else => |status| return windows.unexpectedStatus(status),
//}
switch (windows.ntdll.LdrGetProcedureAddress(dnsapi_dll, &.init(
&.{ 'D', 'n', 's', 'F', 'r', 'e', 'e' },
), 0, @ptrCast(&DnsFree))) {
.SUCCESS => t.dl.DnsFree.store(DnsFree, .release),
else => |status| return windows.unexpectedStatus(status),
}
}
try Thread.checkCancel();
const current_thread = Thread.current;
var lookup_dns: LookupDnsWindows = .{
.threaded = t,
.thread = if (current_thread) |thread| thread.handle else undefined,
.resolved = resolved,
.options = options,
.results = .{
.Version = 1,
.QueryStatus = undefined,
.QueryOptions = undefined,
.pQueryRecords = undefined,
.Reserved = undefined,
},
.done = false,
};
var host_name_w: [HostName.max_len:0]windows.WCHAR = undefined;
host_name_w[
std.unicode.wtf8ToWtf16Le(&host_name_w, name) catch |err| switch (err) {
error.InvalidWtf8 => return error.UnknownHostName,
}
] = 0;
// Workaround various bugs by attempting a synchronous non-wire query first
switch (DnsQueryEx.?(&.{
.Version = 1,
.QueryName = &host_name_w,
.QueryType = if (options.family == .ip4) .A else .AAAA,
.QueryOptions = .{
.NO_WIRE_QUERY = true,
.NO_HOSTS_FILE = true,
.ADDRCONFIG = true,
.DUAL_ADDR = options.family == null,
},
}, &lookup_dns.results, null)) {
.SUCCESS => try lookup_dns.completedFallible(),
.DNS_REQUEST_PENDING => unreachable,
.DNS_ERROR_RECORD_DOES_NOT_EXIST => switch (DnsQueryEx.?(&.{
.Version = 1,
.QueryName = &host_name_w,
.QueryType = if (options.family == .ip4) .A else .AAAA,
.QueryOptions = .{
.NO_HOSTS_FILE = true,
.ADDRCONFIG = true,
.DUAL_ADDR = options.family == null,
.MULTICAST_WAIT = true,
},
.pQueryCompletionCallback = if (current_thread) |_| &LookupDnsWindows.completed else null,
}, &lookup_dns.results,
null)) {
.SUCCESS => try lookup_dns.completedFallible(),
.DNS_REQUEST_PENDING => {
assert(current_thread != null);
while (!@atomicLoad(bool, &lookup_dns.done, .acquire)) {
// operation completes, thereby releasing references to `host_name_w`,
// `lookup_dns.results`, and `cancel_token`.
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
error.Canceled => |e| {
while (!@atomicLoad(bool, &lookup_dns.done, .acquire)) waitForApcOrAlert();
return e;
},
};
waitForApcOrAlert();
alertable_syscall.finish();
}
},
else => |status| lookup_dns.results.QueryStatus = status,
},
else => |status| lookup_dns.results.QueryStatus = status,
}
switch (lookup_dns.results.QueryStatus) {
.SUCCESS => return,
.DNS_REQUEST_PENDING => unreachable,
.INVALID_NAME,
.DNS_ERROR_RCODE_NAME_ERROR,
.DNS_INFO_NO_RECORDS,
.DNS_ERROR_INVALID_NAME_CHAR,
.DNS_ERROR_RECORD_DOES_NOT_EXIST,
=> return error.UnknownHostName,
else => |err| return windows.unexpectedError(err),
}
}
if (native_os == .openbsd) {
}
if (native_os == .freebsd) {
}
if (is_darwin) {
}
if (builtin.link_libc) {
// stuck with getaddrinfo.
var name_buffer: [HostName.max_len:0]u8 = undefined;
@memcpy(name_buffer[0..name.len], name);
name_buffer[name.len] = 0;
const name_c = name_buffer[0..name.len :0];
var port_buffer: [8]u8 = undefined;
const port_c = std.fmt.bufPrintSentinel(&port_buffer, "{d}", .{options.port}, 0) catch unreachable;
const hints: posix.addrinfo = .{
.flags = .{ .CANONNAME = options.canonical_name_buffer != null, .NUMERICSERV = true },
.family = posix.AF.UNSPEC,
.socktype = posix.SOCK.STREAM,
.protocol = posix.IPPROTO.TCP,
.canonname = null,
.addr = null,
.addrlen = 0,
.next = null,
};
var res: ?*posix.addrinfo = null;
const syscall: Syscall = try .start();
while (true) {
switch (posix.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {
@as(posix.system.EAI, @fromBackingInt(@intCast(0))) => {
syscall.finish();
break;
},
.SYSTEM => switch (posix.errno(-1)) {
.INTR => {
try syscall.checkCancel();
continue;
},
else => |e| {
syscall.finish();
return posix.unexpectedErrno(e);
},
},
else => |e| {
syscall.finish();
switch (e) {
.ADDRFAMILY => return error.AddressFamilyUnsupported,
.AGAIN => return error.NameServerFailure,
.FAIL => return error.NameServerFailure,
.FAMILY => return error.AddressFamilyUnsupported,
.MEMORY => return error.SystemResources,
.NODATA => return error.UnknownHostName,
.NONAME => return error.UnknownHostName,
else => return error.Unexpected,
}
},
}
}
defer if (res) |some| posix.system.freeaddrinfo(some);
var it = res;
var canon_name: ?[*:0]const u8 = null;
while (it) |info| : (it = info.next) {
const addr = info.addr orelse continue;
try resolved.putOne(t_io, .{ .address = addressFromPosix(@alignCast(@fieldParentPtr("any", addr))) });
if (info.canonname) |n| {
if (canon_name == null) {
canon_name = n;
}
}
}
if (canon_name) |n| {
if (copyCanon(options.canonical_name_buffer, std.mem.sliceTo(n, 0))) |canon| {
try resolved.putOne(t_io, .{ .canonical_name = canon });
}
}
return;
}
return error.OptionUnsupported;
}