| ... | ... | @@ -13482,21 +13482,80 @@ fn netLookupFallible( |
| 13482 | 13482 | const name = host_name.bytes; |
| 13483 | 13483 | assert(name.len <= HostName.max_len); |
| 13484 | 13484 | |
| 13485 | | if (is_windows) { |
| 13486 | | if (options.family == null) { |
| 13487 | | if (IpAddress.parseIp4(name, options.port)) |addr| { |
| 13488 | | if (copyCanon(options.canonical_name_buffer, name)) |canon| { |
| 13489 | | try resolved.putAll(t_io, &.{ |
| 13490 | | .{ .address = addr }, |
| 13491 | | .{ .canonical_name = canon }, |
| 13492 | | }); |
| 13493 | | } else { |
| 13494 | | try resolved.putOne(t_io, .{ .address = addr }); |
| 13495 | | } |
| 13496 | | return; |
| 13497 | | } else |_| {} |
| 13485 | // On Linux, glibc provides getaddrinfo_a which is capable of supporting our semantics. |
| 13486 | // However, musl's POSIX-compliant getaddrinfo is not, so we bypass it. |
| 13487 | |
| 13488 | if (builtin.target.isGnuLibC()) { |
| 13489 | // TODO use getaddrinfo_a / gai_cancel |
| 13490 | } |
| 13491 | |
| 13492 | if (native_os == .linux or is_windows) { |
| 13493 | if (IpAddress.parseIp6(name, options.port)) |addr| { |
| 13494 | if (options.family == .ip4) return error.UnknownHostName; |
| 13495 | if (copyCanon(options.canonical_name_buffer, name)) |canon| { |
| 13496 | try resolved.putAll(t_io, &.{ |
| 13497 | .{ .address = addr }, |
| 13498 | .{ .canonical_name = canon }, |
| 13499 | }); |
| 13500 | } else { |
| 13501 | try resolved.putOne(t_io, .{ .address = addr }); |
| 13502 | } |
| 13503 | return; |
| 13504 | } else |_| {} |
| 13505 | |
| 13506 | if (IpAddress.parseIp4(name, options.port)) |addr| { |
| 13507 | if (options.family == .ip6) return error.UnknownHostName; |
| 13508 | if (copyCanon(options.canonical_name_buffer, name)) |canon| { |
| 13509 | try resolved.putAll(t_io, &.{ |
| 13510 | .{ .address = addr }, |
| 13511 | .{ .canonical_name = canon }, |
| 13512 | }); |
| 13513 | } else { |
| 13514 | try resolved.putOne(t_io, .{ .address = addr }); |
| 13515 | } |
| 13516 | return; |
| 13517 | } else |_| {} |
| 13518 | |
| 13519 | if (t.lookupHosts(host_name, resolved, options)) return else |err| switch (err) { |
| 13520 | error.UnknownHostName => {}, |
| 13521 | else => |e| return e, |
| 13522 | } |
| 13523 | |
| 13524 | // RFC 6761 Section 6.3.3 |
| 13525 | // Name resolution APIs and libraries SHOULD recognize |
| 13526 | // localhost names as special and SHOULD always return the IP |
| 13527 | // loopback address for address queries and negative responses |
| 13528 | // for all other query types. |
| 13529 | |
| 13530 | // Check for equal to "localhost(.)" or ends in ".localhost(.)" |
| 13531 | const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost"; |
| 13532 | if (std.mem.endsWith(u8, name, localhost) and |
| 13533 | (name.len == localhost.len or name[name.len - localhost.len] == '.')) |
| 13534 | { |
| 13535 | var results_buffer: [3]HostName.LookupResult = undefined; |
| 13536 | var results_index: usize = 0; |
| 13537 | if (options.family != .ip4) { |
| 13538 | results_buffer[results_index] = .{ .address = .{ .ip6 = .loopback(options.port) } }; |
| 13539 | results_index += 1; |
| 13540 | } |
| 13541 | if (options.family != .ip6) { |
| 13542 | results_buffer[results_index] = .{ .address = .{ .ip4 = .loopback(options.port) } }; |
| 13543 | results_index += 1; |
| 13544 | } |
| 13545 | if (options.canonical_name_buffer) |buf| { |
| 13546 | const canon_name = "localhost"; |
| 13547 | const canon_name_dest = buf[0..canon_name.len]; |
| 13548 | canon_name_dest.* = canon_name.*; |
| 13549 | results_buffer[results_index] = .{ .canonical_name = .{ .bytes = canon_name_dest } }; |
| 13550 | results_index += 1; |
| 13551 | } |
| 13552 | try resolved.putAll(t_io, results_buffer[0..results_index]); |
| 13553 | return; |
| 13498 | 13554 | } |
| 13499 | 13555 | |
| 13556 | if (native_os == .linux) return t.lookupDnsSearch(host_name, resolved, options); |
| 13557 | |
| 13558 | comptime assert(is_windows); |
| 13500 | 13559 | var DnsQueryEx = t.dl.DnsQueryEx.load(.acquire); |
| 13501 | 13560 | //var DnsCancelQuery = t.dl.DnsCancelQuery.load(.acquire); |
| 13502 | 13561 | var DnsFree = t.dl.DnsFree.load(.acquire); |
| ... | ... | @@ -13540,6 +13599,7 @@ fn netLookupFallible( |
| 13540 | 13599 | else => |status| return windows.unexpectedStatus(status), |
| 13541 | 13600 | } |
| 13542 | 13601 | } |
| 13602 | try Thread.checkCancel(); |
| 13543 | 13603 | const current_thread = Thread.current; |
| 13544 | 13604 | var lookup_dns: LookupDnsWindows = .{ |
| 13545 | 13605 | .threaded = t, |
| ... | ... | @@ -13562,126 +13622,71 @@ fn netLookupFallible( |
| 13562 | 13622 | } |
| 13563 | 13623 | ] = 0; |
| 13564 | 13624 | //var cancel_token: windows.DNS.QUERY.CANCEL = undefined; |
| 13625 | // Workaround various bugs by attempting a synchronous non-wire query first |
| 13565 | 13626 | switch (DnsQueryEx.?(&.{ |
| 13566 | 13627 | .Version = 1, |
| 13567 | 13628 | .QueryName = &host_name_w, |
| 13568 | 13629 | .QueryType = if (options.family == .ip4) .A else .AAAA, |
| 13569 | 13630 | .QueryOptions = .{ |
| 13631 | .NO_WIRE_QUERY = true, |
| 13632 | .NO_HOSTS_FILE = true, // handled above |
| 13570 | 13633 | .ADDRCONFIG = true, |
| 13571 | 13634 | .DUAL_ADDR = options.family == null, |
| 13572 | | .MULTICAST_WAIT = true, |
| 13573 | 13635 | }, |
| 13574 | | .pQueryCompletionCallback = if (current_thread) |_| &LookupDnsWindows.completed else null, |
| 13575 | | }, &lookup_dns.results, |
| 13576 | | //&cancel_token, |
| 13577 | | null)) { |
| 13636 | }, &lookup_dns.results, null)) { |
| 13637 | .SUCCESS => try lookup_dns.completedFallible(), |
| 13578 | 13638 | // We must wait for the APC routine. |
| 13579 | | .SUCCESS, .DNS_REQUEST_PENDING => |status| if (current_thread) |_| { |
| 13580 | | while (!@atomicLoad(bool, &lookup_dns.done, .acquire)) { |
| 13581 | | // Once we get here we must not return from the function until the |
| 13582 | | // operation completes, thereby releasing references to `host_name_w`, |
| 13583 | | // `lookup_dns.results`, and `cancel_token`. |
| 13584 | | const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { |
| 13585 | | error.Canceled => |e| { |
| 13586 | | //_ = DnsCancelQuery.?(&cancel_token); |
| 13587 | | while (!@atomicLoad(bool, &lookup_dns.done, .acquire)) waitForApcOrAlert(); |
| 13588 | | return e; |
| 13589 | | }, |
| 13590 | | }; |
| 13591 | | waitForApcOrAlert(); |
| 13592 | | alertable_syscall.finish(); |
| 13593 | | } |
| 13594 | | } else switch (status) { |
| 13639 | .DNS_REQUEST_PENDING => unreachable, // `pQueryCompletionCallback` was `null` |
| 13640 | .DNS_ERROR_RECORD_DOES_NOT_EXIST => switch (DnsQueryEx.?(&.{ |
| 13641 | .Version = 1, |
| 13642 | .QueryName = &host_name_w, |
| 13643 | .QueryType = if (options.family == .ip4) .A else .AAAA, |
| 13644 | .QueryOptions = .{ |
| 13645 | .NO_HOSTS_FILE = true, // handled above |
| 13646 | .ADDRCONFIG = true, |
| 13647 | .DUAL_ADDR = options.family == null, |
| 13648 | .MULTICAST_WAIT = true, |
| 13649 | }, |
| 13650 | .pQueryCompletionCallback = if (current_thread) |_| &LookupDnsWindows.completed else null, |
| 13651 | }, &lookup_dns.results, |
| 13652 | //&cancel_token, |
| 13653 | null)) { |
| 13595 | 13654 | .SUCCESS => try lookup_dns.completedFallible(), |
| 13596 | | .DNS_REQUEST_PENDING => unreachable, // `pQueryCompletionCallback` was `null` |
| 13597 | | else => unreachable, |
| 13655 | // We must wait for the APC routine. |
| 13656 | .DNS_REQUEST_PENDING => { |
| 13657 | assert(current_thread != null); // `pQueryCompletionCallback` was `null` |
| 13658 | while (!@atomicLoad(bool, &lookup_dns.done, .acquire)) { |
| 13659 | // Once we get here we must not return from the function until the |
| 13660 | // operation completes, thereby releasing references to `host_name_w`, |
| 13661 | // `lookup_dns.results`, and `cancel_token`. |
| 13662 | const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { |
| 13663 | error.Canceled => |e| { |
| 13664 | //_ = DnsCancelQuery.?(&cancel_token); |
| 13665 | while (!@atomicLoad(bool, &lookup_dns.done, .acquire)) waitForApcOrAlert(); |
| 13666 | return e; |
| 13667 | }, |
| 13668 | }; |
| 13669 | waitForApcOrAlert(); |
| 13670 | alertable_syscall.finish(); |
| 13671 | } |
| 13672 | }, |
| 13673 | else => |status| lookup_dns.results.QueryStatus = status, |
| 13598 | 13674 | }, |
| 13599 | 13675 | else => |status| lookup_dns.results.QueryStatus = status, |
| 13600 | 13676 | } |
| 13601 | 13677 | switch (lookup_dns.results.QueryStatus) { |
| 13602 | 13678 | .SUCCESS => return, |
| 13603 | 13679 | .DNS_REQUEST_PENDING => unreachable, // already handled |
| 13604 | | .INVALID_NAME, .DNS_INFO_NO_RECORDS => return error.UnknownHostName, |
| 13680 | .INVALID_NAME, |
| 13681 | .DNS_ERROR_RCODE_NAME_ERROR, |
| 13682 | .DNS_INFO_NO_RECORDS, |
| 13683 | .DNS_ERROR_INVALID_NAME_CHAR, |
| 13684 | .DNS_ERROR_RECORD_DOES_NOT_EXIST, |
| 13685 | => return error.UnknownHostName, |
| 13605 | 13686 | else => |err| return windows.unexpectedError(err), |
| 13606 | 13687 | } |
| 13607 | 13688 | } |
| 13608 | 13689 | |
| 13609 | | // On Linux, glibc provides getaddrinfo_a which is capable of supporting our semantics. |
| 13610 | | // However, musl's POSIX-compliant getaddrinfo is not, so we bypass it. |
| 13611 | | |
| 13612 | | if (builtin.target.isGnuLibC()) { |
| 13613 | | // TODO use getaddrinfo_a / gai_cancel |
| 13614 | | } |
| 13615 | | |
| 13616 | | if (native_os == .linux) { |
| 13617 | | if (options.family != .ip4) { |
| 13618 | | if (IpAddress.parseIp6(name, options.port)) |addr| { |
| 13619 | | if (copyCanon(options.canonical_name_buffer, name)) |canon| { |
| 13620 | | try resolved.putAll(t_io, &.{ |
| 13621 | | .{ .address = addr }, |
| 13622 | | .{ .canonical_name = canon }, |
| 13623 | | }); |
| 13624 | | } else { |
| 13625 | | try resolved.putOne(t_io, .{ .address = addr }); |
| 13626 | | } |
| 13627 | | return; |
| 13628 | | } else |_| {} |
| 13629 | | } |
| 13630 | | |
| 13631 | | if (options.family != .ip6) { |
| 13632 | | if (IpAddress.parseIp4(name, options.port)) |addr| { |
| 13633 | | if (copyCanon(options.canonical_name_buffer, name)) |canon| { |
| 13634 | | try resolved.putAll(t_io, &.{ |
| 13635 | | .{ .address = addr }, |
| 13636 | | .{ .canonical_name = canon }, |
| 13637 | | }); |
| 13638 | | } else { |
| 13639 | | try resolved.putOne(t_io, .{ .address = addr }); |
| 13640 | | } |
| 13641 | | return; |
| 13642 | | } else |_| {} |
| 13643 | | } |
| 13644 | | |
| 13645 | | t.lookupHosts(host_name, resolved, options) catch |err| switch (err) { |
| 13646 | | error.UnknownHostName => {}, |
| 13647 | | else => |e| return e, |
| 13648 | | }; |
| 13649 | | |
| 13650 | | // RFC 6761 Section 6.3.3 |
| 13651 | | // Name resolution APIs and libraries SHOULD recognize |
| 13652 | | // localhost names as special and SHOULD always return the IP |
| 13653 | | // loopback address for address queries and negative responses |
| 13654 | | // for all other query types. |
| 13655 | | |
| 13656 | | // Check for equal to "localhost(.)" or ends in ".localhost(.)" |
| 13657 | | const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost"; |
| 13658 | | if (std.mem.endsWith(u8, name, localhost) and |
| 13659 | | (name.len == localhost.len or name[name.len - localhost.len] == '.')) |
| 13660 | | { |
| 13661 | | var results_buffer: [3]HostName.LookupResult = undefined; |
| 13662 | | var results_index: usize = 0; |
| 13663 | | if (options.family != .ip4) { |
| 13664 | | results_buffer[results_index] = .{ .address = .{ .ip6 = .loopback(options.port) } }; |
| 13665 | | results_index += 1; |
| 13666 | | } |
| 13667 | | if (options.family != .ip6) { |
| 13668 | | results_buffer[results_index] = .{ .address = .{ .ip4 = .loopback(options.port) } }; |
| 13669 | | results_index += 1; |
| 13670 | | } |
| 13671 | | if (options.canonical_name_buffer) |buf| { |
| 13672 | | const canon_name = "localhost"; |
| 13673 | | const canon_name_dest = buf[0..canon_name.len]; |
| 13674 | | canon_name_dest.* = canon_name.*; |
| 13675 | | results_buffer[results_index] = .{ .canonical_name = .{ .bytes = canon_name_dest } }; |
| 13676 | | results_index += 1; |
| 13677 | | } |
| 13678 | | try resolved.putAll(t_io, results_buffer[0..results_index]); |
| 13679 | | return; |
| 13680 | | } |
| 13681 | | |
| 13682 | | return t.lookupDnsSearch(host_name, resolved, options); |
| 13683 | | } |
| 13684 | | |
| 13685 | 13690 | if (native_os == .openbsd) { |
| 13686 | 13691 | // TODO use getaddrinfo_async / asr_abort |
| 13687 | 13692 | } |
| ... | ... | @@ -14510,8 +14515,32 @@ fn lookupHosts( |
| 14510 | 14515 | resolved: *Io.Queue(HostName.LookupResult), |
| 14511 | 14516 | options: HostName.LookupOptions, |
| 14512 | 14517 | ) !void { |
| 14513 | | const t_io = io(t); |
| 14514 | | const file = Dir.openFileAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) { |
| 14518 | const path_w = if (is_windows) path_w: { |
| 14519 | var path_w_buf: [windows.PATH_MAX_WIDE:0]u16 = undefined; |
| 14520 | const system_dir = windows.getSystemDirectoryWtf16Le(); |
| 14521 | const suffix = [_]u16{ |
| 14522 | '\\', 'd', 'r', 'i', 'v', 'e', 'r', 's', '\\', 'e', 't', 'c', '\\', 'h', 'o', 's', 't', 's', |
| 14523 | }; |
| 14524 | @memcpy(path_w_buf[0..system_dir.len], system_dir); |
| 14525 | @memcpy(path_w_buf[system_dir.len..][0..suffix.len], &suffix); |
| 14526 | path_w_buf[system_dir.len + suffix.len] = 0; |
| 14527 | break :path_w wToPrefixedFileW(null, &path_w_buf, .{}) catch |err| switch (err) { |
| 14528 | error.FileNotFound, |
| 14529 | error.AccessDenied, |
| 14530 | => return error.UnknownHostName, |
| 14531 | |
| 14532 | error.Canceled => |e| return e, |
| 14533 | |
| 14534 | else => { |
| 14535 | // Here we could add more detailed diagnostics to the results queue. |
| 14536 | return error.DetectingNetworkConfigurationFailed; |
| 14537 | }, |
| 14538 | }; |
| 14539 | }; |
| 14540 | const file = (if (is_windows) |
| 14541 | dirOpenFileWtf16(null, path_w.span(), .{}) |
| 14542 | else |
| 14543 | dirOpenFile(t, .cwd(), "/etc/hosts", .{})) catch |err| switch (err) { |
| 14515 | 14544 | error.FileNotFound, |
| 14516 | 14545 | error.NotDir, |
| 14517 | 14546 | error.AccessDenied, |
| ... | ... | @@ -14524,10 +14553,10 @@ fn lookupHosts( |
| 14524 | 14553 | return error.DetectingNetworkConfigurationFailed; |
| 14525 | 14554 | }, |
| 14526 | 14555 | }; |
| 14527 | | defer file.close(t_io); |
| 14556 | defer fileClose(t, &.{file}); |
| 14528 | 14557 | |
| 14529 | 14558 | var line_buf: [512]u8 = undefined; |
| 14530 | | var file_reader = file.reader(t_io, &line_buf); |
| 14559 | var file_reader = file.reader(t.io(), &line_buf); |
| 14531 | 14560 | return t.lookupHostsReader(host_name, resolved, options, &file_reader.interface) catch |err| switch (err) { |
| 14532 | 14561 | error.ReadFailed => switch (file_reader.err.?) { |
| 14533 | 14562 | error.Canceled => |e| return e, |
| ... | ... | @@ -14567,14 +14596,17 @@ fn lookupHostsReader( |
| 14567 | 14596 | error.EndOfStream => break, |
| 14568 | 14597 | }; |
| 14569 | 14598 | reader.toss(@min(1, reader.bufferedLen())); |
| 14570 | | var split_it = std.mem.splitScalar(u8, line, '#'); |
| 14599 | var split_it = std.mem.splitScalar(u8, if (is_windows and std.mem.endsWith(u8, line, "\r")) |
| 14600 | line[0 .. line.len - 1] |
| 14601 | else |
| 14602 | line, '#'); |
| 14571 | 14603 | const no_comment_line = split_it.first(); |
| 14572 | 14604 | |
| 14573 | 14605 | var line_it = std.mem.tokenizeAny(u8, no_comment_line, " \t"); |
| 14574 | 14606 | const ip_text = line_it.next() orelse continue; |
| 14575 | 14607 | var first_name_text: ?[]const u8 = null; |
| 14576 | 14608 | while (line_it.next()) |name_text| { |
| 14577 | | if (std.mem.eql(u8, name_text, host_name.bytes)) { |
| 14609 | if (std.ascii.eqlIgnoreCase(name_text, host_name.bytes)) { |
| 14578 | 14610 | if (first_name_text == null) first_name_text = name_text; |
| 14579 | 14611 | break; |
| 14580 | 14612 | } |