| ... | @@ -13482,21 +13482,80 @@ fn netLookupFallible( | ... | @@ -13482,21 +13482,80 @@ fn netLookupFallible( |
| 13482 | const name = host_name.bytes; | 13482 | const name = host_name.bytes; |
| 13483 | assert(name.len <= HostName.max_len); | 13483 | assert(name.len <= HostName.max_len); |
| 13484 | | 13484 | |
| 13485 | if (is_windows) { | 13485 | // On Linux, glibc provides getaddrinfo_a which is capable of supporting our semantics. |
| 13486 | if (options.family == null) { | 13486 | // However, musl's POSIX-compliant getaddrinfo is not, so we bypass it. |
| 13487 | if (IpAddress.parseIp4(name, options.port)) |addr| { | 13487 | |
| 13488 | if (copyCanon(options.canonical_name_buffer, name)) |canon| { | 13488 | if (builtin.target.isGnuLibC()) { |
| 13489 | try resolved.putAll(t_io, &.{ | 13489 | // TODO use getaddrinfo_a / gai_cancel |
| 13490 | .{ .address = addr }, | 13490 | } |
| 13491 | .{ .canonical_name = canon }, | 13491 | |
| 13492 | }); | 13492 | if (native_os == .linux or is_windows) { |
| 13493 | } else { | 13493 | if (IpAddress.parseIp6(name, options.port)) |addr| { |
| 13494 | try resolved.putOne(t_io, .{ .address = addr }); | 13494 | if (options.family == .ip4) return error.UnknownHostName; |
| 13495 | } | 13495 | if (copyCanon(options.canonical_name_buffer, name)) |canon| { |
| 13496 | return; | 13496 | try resolved.putAll(t_io, &.{ |
| 13497 | } else |_| {} | 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 | var DnsQueryEx = t.dl.DnsQueryEx.load(.acquire); | 13559 | var DnsQueryEx = t.dl.DnsQueryEx.load(.acquire); |
| 13501 | //var DnsCancelQuery = t.dl.DnsCancelQuery.load(.acquire); | 13560 | //var DnsCancelQuery = t.dl.DnsCancelQuery.load(.acquire); |
| 13502 | var DnsFree = t.dl.DnsFree.load(.acquire); | 13561 | var DnsFree = t.dl.DnsFree.load(.acquire); |
| ... | @@ -13540,6 +13599,7 @@ fn netLookupFallible( | ... | @@ -13540,6 +13599,7 @@ fn netLookupFallible( |
| 13540 | else => |status| return windows.unexpectedStatus(status), | 13599 | else => |status| return windows.unexpectedStatus(status), |
| 13541 | } | 13600 | } |
| 13542 | } | 13601 | } |
| | 13602 | try Thread.checkCancel(); |
| 13543 | const current_thread = Thread.current; | 13603 | const current_thread = Thread.current; |
| 13544 | var lookup_dns: LookupDnsWindows = .{ | 13604 | var lookup_dns: LookupDnsWindows = .{ |
| 13545 | .threaded = t, | 13605 | .threaded = t, |
| ... | @@ -13562,126 +13622,71 @@ fn netLookupFallible( | ... | @@ -13562,126 +13622,71 @@ fn netLookupFallible( |
| 13562 | } | 13622 | } |
| 13563 | ] = 0; | 13623 | ] = 0; |
| 13564 | //var cancel_token: windows.DNS.QUERY.CANCEL = undefined; | 13624 | //var cancel_token: windows.DNS.QUERY.CANCEL = undefined; |
| | 13625 | // Workaround various bugs by attempting a synchronous non-wire query first |
| 13565 | switch (DnsQueryEx.?(&.{ | 13626 | switch (DnsQueryEx.?(&.{ |
| 13566 | .Version = 1, | 13627 | .Version = 1, |
| 13567 | .QueryName = &host_name_w, | 13628 | .QueryName = &host_name_w, |
| 13568 | .QueryType = if (options.family == .ip4) .A else .AAAA, | 13629 | .QueryType = if (options.family == .ip4) .A else .AAAA, |
| 13569 | .QueryOptions = .{ | 13630 | .QueryOptions = .{ |
| | 13631 | .NO_WIRE_QUERY = true, |
| | 13632 | .NO_HOSTS_FILE = true, // handled above |
| 13570 | .ADDRCONFIG = true, | 13633 | .ADDRCONFIG = true, |
| 13571 | .DUAL_ADDR = options.family == null, | 13634 | .DUAL_ADDR = options.family == null, |
| 13572 | .MULTICAST_WAIT = true, | | |
| 13573 | }, | 13635 | }, |
| 13574 | .pQueryCompletionCallback = if (current_thread) |_| &LookupDnsWindows.completed else null, | 13636 | }, &lookup_dns.results, null)) { |
| 13575 | }, &lookup_dns.results, | 13637 | .SUCCESS => try lookup_dns.completedFallible(), |
| 13576 | //&cancel_token, | | |
| 13577 | null)) { | | |
| 13578 | // We must wait for the APC routine. | 13638 | // We must wait for the APC routine. |
| 13579 | .SUCCESS, .DNS_REQUEST_PENDING => |status| if (current_thread) |_| { | 13639 | .DNS_REQUEST_PENDING => unreachable, // `pQueryCompletionCallback` was `null` |
| 13580 | while (!@atomicLoad(bool, &lookup_dns.done, .acquire)) { | 13640 | .DNS_ERROR_RECORD_DOES_NOT_EXIST => switch (DnsQueryEx.?(&.{ |
| 13581 | // Once we get here we must not return from the function until the | 13641 | .Version = 1, |
| 13582 | // operation completes, thereby releasing references to `host_name_w`, | 13642 | .QueryName = &host_name_w, |
| 13583 | // `lookup_dns.results`, and `cancel_token`. | 13643 | .QueryType = if (options.family == .ip4) .A else .AAAA, |
| 13584 | const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { | 13644 | .QueryOptions = .{ |
| 13585 | error.Canceled => |e| { | 13645 | .NO_HOSTS_FILE = true, // handled above |
| 13586 | //_ = DnsCancelQuery.?(&cancel_token); | 13646 | .ADDRCONFIG = true, |
| 13587 | while (!@atomicLoad(bool, &lookup_dns.done, .acquire)) waitForApcOrAlert(); | 13647 | .DUAL_ADDR = options.family == null, |
| 13588 | return e; | 13648 | .MULTICAST_WAIT = true, |
| 13589 | }, | 13649 | }, |
| 13590 | }; | 13650 | .pQueryCompletionCallback = if (current_thread) |_| &LookupDnsWindows.completed else null, |
| 13591 | waitForApcOrAlert(); | 13651 | }, &lookup_dns.results, |
| 13592 | alertable_syscall.finish(); | 13652 | //&cancel_token, |
| 13593 | } | 13653 | null)) { |
| 13594 | } else switch (status) { | | |
| 13595 | .SUCCESS => try lookup_dns.completedFallible(), | 13654 | .SUCCESS => try lookup_dns.completedFallible(), |
| 13596 | .DNS_REQUEST_PENDING => unreachable, // `pQueryCompletionCallback` was `null` | 13655 | // We must wait for the APC routine. |
| 13597 | else => unreachable, | 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 | else => |status| lookup_dns.results.QueryStatus = status, | 13675 | else => |status| lookup_dns.results.QueryStatus = status, |
| 13600 | } | 13676 | } |
| 13601 | switch (lookup_dns.results.QueryStatus) { | 13677 | switch (lookup_dns.results.QueryStatus) { |
| 13602 | .SUCCESS => return, | 13678 | .SUCCESS => return, |
| 13603 | .DNS_REQUEST_PENDING => unreachable, // already handled | 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 | else => |err| return windows.unexpectedError(err), | 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 | if (native_os == .openbsd) { | 13690 | if (native_os == .openbsd) { |
| 13686 | // TODO use getaddrinfo_async / asr_abort | 13691 | // TODO use getaddrinfo_async / asr_abort |
| 13687 | } | 13692 | } |
| ... | @@ -14510,8 +14515,32 @@ fn lookupHosts( | ... | @@ -14510,8 +14515,32 @@ fn lookupHosts( |
| 14510 | resolved: *Io.Queue(HostName.LookupResult), | 14515 | resolved: *Io.Queue(HostName.LookupResult), |
| 14511 | options: HostName.LookupOptions, | 14516 | options: HostName.LookupOptions, |
| 14512 | ) !void { | 14517 | ) !void { |
| 14513 | const t_io = io(t); | 14518 | const path_w = if (is_windows) path_w: { |
| 14514 | const file = Dir.openFileAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) { | 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 | error.FileNotFound, | 14544 | error.FileNotFound, |
| 14516 | error.NotDir, | 14545 | error.NotDir, |
| 14517 | error.AccessDenied, | 14546 | error.AccessDenied, |
| ... | @@ -14524,10 +14553,10 @@ fn lookupHosts( | ... | @@ -14524,10 +14553,10 @@ fn lookupHosts( |
| 14524 | return error.DetectingNetworkConfigurationFailed; | 14553 | return error.DetectingNetworkConfigurationFailed; |
| 14525 | }, | 14554 | }, |
| 14526 | }; | 14555 | }; |
| 14527 | defer file.close(t_io); | 14556 | defer fileClose(t, &.{file}); |
| 14528 | | 14557 | |
| 14529 | var line_buf: [512]u8 = undefined; | 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 | return t.lookupHostsReader(host_name, resolved, options, &file_reader.interface) catch |err| switch (err) { | 14560 | return t.lookupHostsReader(host_name, resolved, options, &file_reader.interface) catch |err| switch (err) { |
| 14532 | error.ReadFailed => switch (file_reader.err.?) { | 14561 | error.ReadFailed => switch (file_reader.err.?) { |
| 14533 | error.Canceled => |e| return e, | 14562 | error.Canceled => |e| return e, |
| ... | @@ -14567,14 +14596,17 @@ fn lookupHostsReader( | ... | @@ -14567,14 +14596,17 @@ fn lookupHostsReader( |
| 14567 | error.EndOfStream => break, | 14596 | error.EndOfStream => break, |
| 14568 | }; | 14597 | }; |
| 14569 | reader.toss(@min(1, reader.bufferedLen())); | 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 | const no_comment_line = split_it.first(); | 14603 | const no_comment_line = split_it.first(); |
| 14572 | | 14604 | |
| 14573 | var line_it = std.mem.tokenizeAny(u8, no_comment_line, " \t"); | 14605 | var line_it = std.mem.tokenizeAny(u8, no_comment_line, " \t"); |
| 14574 | const ip_text = line_it.next() orelse continue; | 14606 | const ip_text = line_it.next() orelse continue; |
| 14575 | var first_name_text: ?[]const u8 = null; | 14607 | var first_name_text: ?[]const u8 = null; |
| 14576 | while (line_it.next()) |name_text| { | 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 | if (first_name_text == null) first_name_text = name_text; | 14610 | if (first_name_text == null) first_name_text = name_text; |
| 14579 | break; | 14611 | break; |
| 14580 | } | 14612 | } |