authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-23 16:52:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:28-07:00
log396464ee6b8f5534947e5f457dbd2a26a458cb0f
tree0b31766335cc1bc733d130e0cc00c782aacdf7b4
parent1bb75f9d6276ddf6b69717d9b1c2f68140727425

update std.net and nail down delimiter APIs

"exclusive" functions still need to report EndOfStream after the last returned slice

7 files changed, 441 insertions(+), 372 deletions(-)

lib/std/compress/flate/inflate.zig+1-1
......@@ -681,7 +681,7 @@ pub fn BitReader(comptime T: type) type {
681681 (self.nbits >> 3); // 0 for 0-7, 1 for 8-16, ... same as / 8
682682
683683 var buf: [t_bytes]u8 = [_]u8{0} ** t_bytes;
684 const bytes_read = self.forward_reader.readShort(buf[0..empty_bytes]) catch 0;
684 const bytes_read = self.forward_reader.readSliceShort(buf[0..empty_bytes]) catch 0;
685685 if (bytes_read > 0) {
686686 const u: T = std.mem.readInt(T, buf[0..t_bytes], .little);
687687 self.bits |= u << @as(Tshift, @intCast(self.nbits));
lib/std/crypto/Certificate/Bundle.zig+6-2
......@@ -225,7 +225,9 @@ pub const AddCertsFromFileError = Allocator.Error ||
225225 error{ CertificateAuthorityBundleTooBig, MissingEndCertificateMarker };
226226
227227pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFromFileError!void {
228 const size = try file.getEndPos();
228 var file_reader = file.reader();
229 const size = try file_reader.getSize();
230 var br = file_reader.interface().unbuffered();
229231
230232 // We borrow `bytes` as a temporary buffer for the base64-encoded data.
231233 // This is possible by computing the decoded length and reserving the space
......@@ -236,7 +238,9 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFrom
236238 try cb.bytes.ensureUnusedCapacity(gpa, needed_capacity);
237239 const end_reserved: u32 = @intCast(cb.bytes.items.len + decoded_size_upper_bound);
238240 const buffer = cb.bytes.allocatedSlice()[end_reserved..];
239 const end_index = try file.readShort(buffer);
241 const end_index = br.readSliceShort(buffer) catch |err| switch (err) {
242 error.ReadFailed => return file_reader.err.?,
243 };
240244 const encoded_bytes = buffer[0..end_index];
241245
242246 const begin_marker = "-----BEGIN CERTIFICATE-----";
lib/std/fs/Dir.zig+4-18
......@@ -1947,22 +1947,6 @@ pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
19471947 return windows.ReadLink(self.fd, sub_path_w, buffer);
19481948}
19491949
1950/// Read all of file contents using a preallocated buffer.
1951/// The returned slice has the same pointer as `buffer`. If the length matches `buffer.len`
1952/// the situation is ambiguous. It could either mean that the entire file was read, and
1953/// it exactly fits the buffer, or it could mean the buffer was not big enough for the
1954/// entire file.
1955/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1956/// On WASI, `file_path` should be encoded as valid UTF-8.
1957/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
1958pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 {
1959 var file = try self.openFile(file_path, .{});
1960 defer file.close();
1961
1962 const end_index = try file.readAll(buffer);
1963 return buffer[0..end_index];
1964}
1965
19661950pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{StreamTooLong};
19671951
19681952/// Reads all the bytes from the named file. On success, caller owns returned
......@@ -2046,10 +2030,13 @@ pub fn readFileIntoArrayList(
20462030 var file = try dir.openFile(file_path, .{});
20472031 defer file.close();
20482032
2033 var file_reader = file.reader();
2034
20492035 // Apply size hint by adjusting the array list's capacity.
20502036 if (size_hint) |size| {
20512037 try list.ensureUnusedCapacity(gpa, size);
2052 } else if (file.getEndPos()) |size| {
2038 file_reader.size = size;
2039 } else if (file_reader.getSize()) |size| {
20532040 // If the file size doesn't fit a usize it'll be certainly exceed the limit.
20542041 try list.ensureUnusedCapacity(gpa, std.math.cast(usize, size) orelse return error.StreamTooLong);
20552042 } else |err| switch (err) {
......@@ -2058,7 +2045,6 @@ pub fn readFileIntoArrayList(
20582045 else => |e| return e,
20592046 }
20602047
2061 var file_reader = file.reader();
20622048 file_reader.interface().readRemainingArrayList(gpa, alignment, list, limit) catch |err| switch (err) {
20632049 error.OutOfMemory => return error.OutOfMemory,
20642050 error.StreamTooLong => return error.StreamTooLong,
lib/std/fs/File.zig+13-14
......@@ -799,20 +799,6 @@ pub fn read(self: File, buffer: []u8) ReadError!usize {
799799 return posix.read(self.handle, buffer);
800800}
801801
802/// One-shot alternative to `std.io.BufferedReader.readShort` via `reader`.
803///
804/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
805/// means the file reached the end.
806pub fn readShort(self: File, buffer: []u8) ReadError!usize {
807 var index: usize = 0;
808 while (index != buffer.len) {
809 const n = try self.read(buffer[index..]);
810 if (n == 0) break;
811 index += n;
812 }
813 return index;
814}
815
816802/// On Windows, this function currently does alter the file pointer.
817803/// https://github.com/ziglang/zig/issues/12783
818804pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
......@@ -948,6 +934,19 @@ pub const Reader = struct {
948934 };
949935 }
950936
937 pub fn getSize(r: *Reader) GetEndPosError!u64 {
938 return r.size orelse {
939 if (r.size_err) |err| return err;
940 if (r.file.getEndPos()) |size| {
941 r.size = size;
942 return size;
943 } else |err| {
944 r.size_err = err;
945 return err;
946 }
947 };
948 }
949
951950 /// Number of slices to store on the stack, when trying to send as many byte
952951 /// vectors through the underlying read calls as possible.
953952 const max_buffers_len = 16;
lib/std/http/test.zig+14-8
......@@ -113,8 +113,9 @@ test "HTTP server handles a chunked transfer coding request" {
113113 try expect(request.head.transfer_encoding == .chunked);
114114
115115 var buf: [128]u8 = undefined;
116 const n = try (try request.reader()).readAll(&buf);
117 try expect(mem.eql(u8, buf[0..n], "ABCD"));
116 var br = (try request.reader()).unbuffered();
117 const n = try br.readSliceShort(&buf);
118 try expectEqualStrings("ABCD", buf[0..n]);
118119
119120 try request.respond("message from server!\n", .{
120121 .extra_headers = &.{
......@@ -143,7 +144,8 @@ test "HTTP server handles a chunked transfer coding request" {
143144 const gpa = std.testing.allocator;
144145 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
145146 defer stream.close();
146 var writer = stream.writer().unbuffered();
147 var stream_writer = stream.writer();
148 var writer = stream_writer.interface().unbuffered();
147149 try writer.writeAll(request_bytes);
148150
149151 const expected_response =
......@@ -153,7 +155,8 @@ test "HTTP server handles a chunked transfer coding request" {
153155 "content-type: text/plain\r\n" ++
154156 "\r\n" ++
155157 "message from server!\n";
156 const response = try stream.reader().readRemainingAlloc(gpa, expected_response.len);
158 var stream_reader = stream.reader();
159 const response = try stream_reader.interface().readRemainingAlloc(gpa, .limited(expected_response.len));
157160 defer gpa.free(response);
158161 try expectEqualStrings(expected_response, response);
159162}
......@@ -206,7 +209,7 @@ test "echo content server" {
206209 // request.head.target,
207210 //});
208211
209 const body = try (try request.reader()).readRemainingAlloc(std.testing.allocator, 8192);
212 const body = try (try request.reader()).readRemainingAlloc(std.testing.allocator, .limited(8192));
210213 defer std.testing.allocator.free(body);
211214
212215 try expect(mem.startsWith(u8, request.head.target, "/echo-content"));
......@@ -288,10 +291,12 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
288291 const gpa = std.testing.allocator;
289292 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
290293 defer stream.close();
291 var writer = stream.writer().unbuffered();
294 var stream_writer = stream.writer();
295 var writer = stream_writer.interface().unbuffered();
292296 try writer.writeAll(request_bytes);
293297
294 const response = try stream.reader().readRemainingAlloc(gpa, 8192);
298 var stream_reader = stream.reader();
299 const response = try stream_reader.interface().readRemainingAlloc(gpa, .limited(8192));
295300 defer gpa.free(response);
296301
297302 var expected_response = std.ArrayList(u8).init(gpa);
......@@ -362,7 +367,8 @@ test "receiving arbitrary http headers from the client" {
362367 var writer = stream_writer.interface().unbuffered();
363368 try writer.writeAll(request_bytes);
364369
365 const response = try stream.reader().readRemainingAlloc(gpa, .limited(8192));
370 var stream_reader = stream.reader();
371 const response = try stream_reader.interface().readRemainingAlloc(gpa, .limited(8192));
366372 defer gpa.free(response);
367373
368374 var expected_response = std.ArrayList(u8).init(gpa);
lib/std/io/BufferedReader.zig+82-56
......@@ -368,7 +368,7 @@ pub fn readSlice(br: *BufferedReader, buffer: []u8) Reader.Error!void {
368368
369369/// Returns the number of bytes read, which is less than `buffer.len` if and
370370/// only if the stream reached the end.
371pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize {
371pub fn readSliceShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize {
372372 _ = br;
373373 _ = buffer;
374374 @panic("TODO");
......@@ -467,10 +467,12 @@ pub fn readRemainingArrayList(
467467 }
468468}
469469
470pub const DelimiterInclusiveError = error{
470pub const DelimiterError = error{
471471 /// See the `Reader` implementation for detailed diagnostics.
472472 ReadFailed,
473 /// Stream ended before the delimiter was found.
473 /// For "inclusive" functions, stream ended before the delimiter was found.
474 /// For "exclusive" functions, stream ended and there are no more bytes to
475 /// return.
474476 EndOfStream,
475477 /// The delimiter was not found within a number of bytes matching the
476478 /// capacity of the `BufferedReader`.
......@@ -488,13 +490,13 @@ pub const DelimiterInclusiveError = error{
488490/// * `peekSentinel`
489491/// * `takeDelimiterExclusive`
490492/// * `takeDelimiterInclusive`
491pub fn takeSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterInclusiveError![:sentinel]u8 {
493pub fn takeSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterError![:sentinel]u8 {
492494 const result = try br.peekSentinel(sentinel);
493495 br.toss(result.len + 1);
494496 return result;
495497}
496498
497pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterInclusiveError![:sentinel]u8 {
499pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterError![:sentinel]u8 {
498500 const result = try br.takeDelimiterInclusive(sentinel);
499501 return result[0 .. result.len - 1 :sentinel];
500502}
......@@ -510,58 +512,24 @@ pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterInclusi
510512/// * `takeSentinel`
511513/// * `takeDelimiterExclusive`
512514/// * `peekDelimiterInclusive`
513pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError![]u8 {
515pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterError![]u8 {
514516 const result = try br.peekDelimiterInclusive(delimiter);
515517 br.toss(result.len);
516518 return result;
517519}
518520
519pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError![]u8 {
520 return (try br.peekDelimiterInclusiveUnlessEnd(delimiter)) orelse error.EndOfStream;
521}
522
523pub const DelimiterExclusiveError = error{
524 /// See the `Reader` implementation for detailed diagnostics.
525 ReadFailed,
526 /// The delimiter was not found within a number of bytes matching the
527 /// capacity of the `BufferedReader`.
528 StreamTooLong,
529};
530
531521/// Returns a slice of the next bytes of buffered data from the stream until
532/// `delimiter` is found, advancing the seek position.
533///
534/// Returned slice excludes the delimiter.
522/// `delimiter` is found, without advancing the seek position.
535523///
536/// End-of-stream is treated equivalent to a delimiter.
524/// Returned slice includes the delimiter as the last byte.
537525///
538526/// Invalidates previously returned values from `peek`.
539527///
540528/// See also:
541/// * `takeSentinel`
542/// * `takeDelimiterInclusive`
529/// * `peekSentinel`
543530/// * `peekDelimiterExclusive`
544pub fn takeDelimiterExclusive(br: *BufferedReader, delimiter: u8) DelimiterExclusiveError![]u8 {
545 const result = br.peekDelimiterInclusiveUnlessEnd(delimiter) catch |err| switch (err) {
546 error.EndOfStream => {
547 br.toss(br.end);
548 return br.buffer[0..br.end];
549 },
550 else => |e| return e,
551 };
552 br.toss(result.len);
553 return result[0 .. result.len - 1];
554}
555
556pub fn peekDelimiterExclusive(br: *BufferedReader, delimiter: u8) DelimiterExclusiveError![]u8 {
557 const result = br.peekDelimiterInclusiveUnlessEnd(delimiter) catch |err| switch (err) {
558 error.EndOfStream => return br.buffer[0..br.end],
559 else => |e| return e,
560 };
561 return result[0 .. result.len - 1];
562}
563
564fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError!?[]u8 {
531/// * `takeDelimiterInclusive`
532pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterError![]u8 {
565533 const buffer = br.buffer[0..br.end];
566534 const seek = br.seek;
567535 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {
......@@ -585,12 +553,70 @@ fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) Delimiter
585553 return error.StreamTooLong;
586554}
587555
556/// Returns a slice of the next bytes of buffered data from the stream until
557/// `delimiter` is found, advancing the seek position.
558///
559/// Returned slice excludes the delimiter. End-of-stream is treated equivalent
560/// to a delimiter, unless it would result in a length 0 return value, in which
561/// case `error.EndOfStream` is returned instead.
562///
563/// If the delimiter is not found within a number of bytes matching the
564/// capacity of this `BufferedReader`, `error.StreamTooLong` is returned. In
565/// such case, the stream state is unmodified as if this function was never
566/// called.
567///
568/// Invalidates previously returned values from `peek`.
569///
570/// See also:
571/// * `takeDelimiterInclusive`
572/// * `peekDelimiterExclusive`
573pub fn takeDelimiterExclusive(br: *BufferedReader, delimiter: u8) DelimiterError![]u8 {
574 const result = br.peekDelimiterInclusive(delimiter) catch |err| switch (err) {
575 error.EndOfStream => {
576 if (br.end == 0) return error.EndOfStream;
577 br.toss(br.end);
578 return br.buffer[0..br.end];
579 },
580 else => |e| return e,
581 };
582 br.toss(result.len);
583 return result[0 .. result.len - 1];
584}
585
586/// Returns a slice of the next bytes of buffered data from the stream until
587/// `delimiter` is found, without advancing the seek position.
588///
589/// Returned slice excludes the delimiter. End-of-stream is treated equivalent
590/// to a delimiter, unless it would result in a length 0 return value, in which
591/// case `error.EndOfStream` is returned instead.
592///
593/// If the delimiter is not found within a number of bytes matching the
594/// capacity of this `BufferedReader`, `error.StreamTooLong` is returned. In
595/// such case, the stream state is unmodified as if this function was never
596/// called.
597///
598/// Invalidates previously returned values from `peek`.
599///
600/// See also:
601/// * `peekDelimiterInclusive`
602/// * `takeDelimiterExclusive`
603pub fn peekDelimiterExclusive(br: *BufferedReader, delimiter: u8) DelimiterError![]u8 {
604 const result = br.peekDelimiterInclusive(delimiter) catch |err| switch (err) {
605 error.EndOfStream => {
606 if (br.end == 0) return error.EndOfStream;
607 return br.buffer[0..br.end];
608 },
609 else => |e| return e,
610 };
611 return result[0 .. result.len - 1];
612}
613
588614/// Appends to `bw` contents by reading from the stream until `delimiter` is
589615/// found. Does not write the delimiter itself.
590616///
591617/// Returns number of bytes streamed.
592pub fn streamToDelimiter(br: *BufferedReader, bw: *BufferedWriter, delimiter: u8) Reader.RwError!usize {
593 const amount, const to = try br.streamToAny(bw, delimiter, .unlimited);
618pub fn readDelimiter(br: *BufferedReader, bw: *BufferedWriter, delimiter: u8) Reader.RwError!usize {
619 const amount, const to = try br.readAny(bw, delimiter, .unlimited);
594620 return switch (to) {
595621 .delimiter => amount,
596622 .limit => unreachable,
......@@ -604,12 +630,12 @@ pub fn streamToDelimiter(br: *BufferedReader, bw: *BufferedWriter, delimiter: u8
604630/// Succeeds if stream ends before delimiter found.
605631///
606632/// Returns number of bytes streamed. The end is not signaled to the writer.
607pub fn streamToDelimiterOrEnd(
633pub fn readDelimiterEnding(
608634 br: *BufferedReader,
609635 bw: *BufferedWriter,
610636 delimiter: u8,
611637) Reader.RwAllError!usize {
612 const amount, const to = try br.streamToAny(bw, delimiter, .unlimited);
638 const amount, const to = try br.readAny(bw, delimiter, .unlimited);
613639 return switch (to) {
614640 .delimiter, .end => amount,
615641 .limit => unreachable,
......@@ -627,13 +653,13 @@ pub const StreamDelimiterLimitedError = Reader.RwAllError || error{
627653/// Does not write the delimiter itself.
628654///
629655/// Returns number of bytes streamed.
630pub fn streamToDelimiterOrLimit(
656pub fn readDelimiterLimit(
631657 br: *BufferedReader,
632658 bw: *BufferedWriter,
633659 delimiter: u8,
634660 limit: Reader.Limit,
635661) StreamDelimiterLimitedError!usize {
636 const amount, const to = try br.streamToAny(bw, delimiter, limit);
662 const amount, const to = try br.readAny(bw, delimiter, limit);
637663 return switch (to) {
638664 .delimiter => amount,
639665 .limit => error.StreamTooLong,
......@@ -641,7 +667,7 @@ pub fn streamToDelimiterOrLimit(
641667 };
642668}
643669
644fn streamToAny(
670fn readAny(
645671 br: *BufferedReader,
646672 bw: *BufferedWriter,
647673 delimiter: ?u8,
......@@ -971,15 +997,15 @@ test peekDelimiterExclusive {
971997 return error.Unimplemented;
972998}
973999
974test streamToDelimiter {
1000test readDelimiter {
9751001 return error.Unimplemented;
9761002}
9771003
978test streamToDelimiterOrEnd {
1004test readDelimiterEnding {
9791005 return error.Unimplemented;
9801006}
9811007
982test streamToDelimiterOrLimit {
1008test readDelimiterLimit {
9831009 return error.Unimplemented;
9841010}
9851011
......@@ -1035,7 +1061,7 @@ test takeLeb128 {
10351061 return error.Unimplemented;
10361062}
10371063
1038test readShort {
1064test readSliceShort {
10391065 return error.Unimplemented;
10401066}
10411067
lib/std/net.zig+321-273
......@@ -11,6 +11,8 @@ const io = std.io;
1111const native_endian = builtin.target.cpu.arch.endian();
1212const native_os = builtin.os.tag;
1313const windows = std.os.windows;
14const Allocator = std.mem.Allocator;
15const ArrayList = std.ArrayListUnmanaged;
1416
1517// Windows 10 added support for unix sockets in build 17063, redstone 4 is the
1618// first release to support them.
......@@ -818,7 +820,7 @@ pub const AddressList = struct {
818820pub const TcpConnectToHostError = GetAddressListError || TcpConnectToAddressError;
819821
820822/// All memory allocated with `allocator` will be freed before this function returns.
821pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream {
823pub fn tcpConnectToHost(allocator: Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream {
822824 const list = try getAddressList(allocator, name, port);
823825 defer list.deinit();
824826
......@@ -851,7 +853,7 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream {
851853
852854// TODO: Instead of having a massive error set, make the error set have categories, and then
853855// store the sub-error as a diagnostic value.
854const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{
856const GetAddressListError = Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{
855857 TemporaryNameServerFailure,
856858 NameServerFailure,
857859 AddressFamilyNotSupported,
......@@ -871,12 +873,13 @@ const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError ||
871873
872874 InterfaceNotFound,
873875 FileSystem,
876 ResolveConfParseFailed,
874877};
875878
876879/// Call `AddressList.deinit` on the result.
877pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList {
880pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList {
878881 const result = blk: {
879 var arena = std.heap.ArenaAllocator.init(allocator);
882 var arena = std.heap.ArenaAllocator.init(gpa);
880883 errdefer arena.deinit();
881884
882885 const result = try arena.allocator().create(AddressList);
......@@ -891,11 +894,11 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
891894 errdefer result.deinit();
892895
893896 if (native_os == .windows) {
894 const name_c = try allocator.dupeZ(u8, name);
895 defer allocator.free(name_c);
897 const name_c = try gpa.dupeZ(u8, name);
898 defer gpa.free(name_c);
896899
897 const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});
898 defer allocator.free(port_c);
900 const port_c = try std.fmt.allocPrintZ(gpa, "{}", .{port});
901 defer gpa.free(port_c);
899902
900903 const ws2_32 = windows.ws2_32;
901904 const hints: posix.addrinfo = .{
......@@ -963,11 +966,11 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
963966 }
964967
965968 if (builtin.link_libc) {
966 const name_c = try allocator.dupeZ(u8, name);
967 defer allocator.free(name_c);
969 const name_c = try gpa.dupeZ(u8, name);
970 defer gpa.free(name_c);
968971
969 const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});
970 defer allocator.free(port_c);
972 const port_c = try std.fmt.allocPrintZ(gpa, "{}", .{port});
973 defer gpa.free(port_c);
971974
972975 const hints: posix.addrinfo = .{
973976 .flags = .{ .NUMERICSERV = true },
......@@ -1030,17 +1033,17 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
10301033
10311034 if (native_os == .linux) {
10321035 const family = posix.AF.UNSPEC;
1033 var lookup_addrs = std.ArrayList(LookupAddr).init(allocator);
1034 defer lookup_addrs.deinit();
1036 var lookup_addrs: ArrayList(LookupAddr) = .empty;
1037 defer lookup_addrs.deinit(gpa);
10351038
1036 var canon = std.ArrayList(u8).init(arena);
1037 defer canon.deinit();
1039 var canon: ArrayList(u8) = .empty;
1040 defer canon.deinit(gpa);
10381041
1039 try linuxLookupName(&lookup_addrs, &canon, name, family, .{ .NUMERICSERV = true }, port);
1042 try linuxLookupName(gpa, &lookup_addrs, &canon, name, family, .{ .NUMERICSERV = true }, port);
10401043
10411044 result.addrs = try arena.alloc(Address, lookup_addrs.items.len);
10421045 if (canon.items.len != 0) {
1043 result.canon_name = try canon.toOwnedSlice();
1046 result.canon_name = try arena.dupe(u8, canon.items);
10441047 }
10451048
10461049 for (lookup_addrs.items, 0..) |lookup_addr, i| {
......@@ -1067,8 +1070,9 @@ const DAS_PREFIX_SHIFT = 8;
10671070const DAS_ORDER_SHIFT = 0;
10681071
10691072fn linuxLookupName(
1070 addrs: *std.ArrayList(LookupAddr),
1071 canon: *std.ArrayList(u8),
1073 gpa: Allocator,
1074 addrs: *ArrayList(LookupAddr),
1075 canon: *ArrayList(u8),
10721076 opt_name: ?[]const u8,
10731077 family: posix.sa_family_t,
10741078 flags: posix.AI,
......@@ -1077,13 +1081,13 @@ fn linuxLookupName(
10771081 if (opt_name) |name| {
10781082 // reject empty name and check len so it fits into temp bufs
10791083 canon.items.len = 0;
1080 try canon.appendSlice(name);
1084 try canon.appendSlice(gpa, name);
10811085 if (Address.parseExpectingFamily(name, family, port)) |addr| {
1082 try addrs.append(LookupAddr{ .addr = addr });
1086 try addrs.append(gpa, .{ .addr = addr });
10831087 } else |name_err| if (flags.NUMERICHOST) {
10841088 return name_err;
10851089 } else {
1086 try linuxLookupNameFromHosts(addrs, canon, name, family, port);
1090 try linuxLookupNameFromHosts(gpa, addrs, canon, name, family, port);
10871091 if (addrs.items.len == 0) {
10881092 // RFC 6761 Section 6.3.3
10891093 // Name resolution APIs and libraries SHOULD recognize localhost
......@@ -1094,17 +1098,18 @@ fn linuxLookupName(
10941098 // Check for equal to "localhost(.)" or ends in ".localhost(.)"
10951099 const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost";
10961100 if (mem.endsWith(u8, name, localhost) and (name.len == localhost.len or name[name.len - localhost.len] == '.')) {
1097 try addrs.append(LookupAddr{ .addr = .{ .in = Ip4Address.parse("127.0.0.1", port) catch unreachable } });
1098 try addrs.append(LookupAddr{ .addr = .{ .in6 = Ip6Address.parse("::1", port) catch unreachable } });
1101 try addrs.append(gpa, .{ .addr = .{ .in = Ip4Address.parse("127.0.0.1", port) catch unreachable } });
1102 try addrs.append(gpa, .{ .addr = .{ .in6 = Ip6Address.parse("::1", port) catch unreachable } });
10991103 return;
11001104 }
11011105
1102 try linuxLookupNameFromDnsSearch(addrs, canon, name, family, port);
1106 try linuxLookupNameFromDnsSearch(gpa, addrs, canon, name, family, port);
11031107 }
11041108 }
11051109 } else {
1106 try canon.resize(0);
1107 try linuxLookupNameFromNull(addrs, family, flags, port);
1110 try canon.resize(gpa, 0);
1111 try addrs.ensureUnusedCapacity(gpa, 1);
1112 linuxLookupNameFromNull(addrs, family, flags, port);
11081113 }
11091114 if (addrs.items.len == 0) return error.UnknownHostName;
11101115
......@@ -1310,39 +1315,40 @@ fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool {
13101315}
13111316
13121317fn linuxLookupNameFromNull(
1313 addrs: *std.ArrayList(LookupAddr),
1318 addrs: *ArrayList(LookupAddr),
13141319 family: posix.sa_family_t,
13151320 flags: posix.AI,
13161321 port: u16,
1317) !void {
1322) void {
13181323 if (flags.PASSIVE) {
13191324 if (family != posix.AF.INET6) {
1320 (try addrs.addOne()).* = LookupAddr{
1325 addrs.appendAssumeCapacity(.{
13211326 .addr = Address.initIp4([1]u8{0} ** 4, port),
1322 };
1327 });
13231328 }
13241329 if (family != posix.AF.INET) {
1325 (try addrs.addOne()).* = LookupAddr{
1330 addrs.appendAssumeCapacity(.{
13261331 .addr = Address.initIp6([1]u8{0} ** 16, port, 0, 0),
1327 };
1332 });
13281333 }
13291334 } else {
13301335 if (family != posix.AF.INET6) {
1331 (try addrs.addOne()).* = LookupAddr{
1336 addrs.appendAssumeCapacity(.{
13321337 .addr = Address.initIp4([4]u8{ 127, 0, 0, 1 }, port),
1333 };
1338 });
13341339 }
13351340 if (family != posix.AF.INET) {
1336 (try addrs.addOne()).* = LookupAddr{
1341 addrs.appendAssumeCapacity(.{
13371342 .addr = Address.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0),
1338 };
1343 });
13391344 }
13401345 }
13411346}
13421347
13431348fn linuxLookupNameFromHosts(
1344 addrs: *std.ArrayList(LookupAddr),
1345 canon: *std.ArrayList(u8),
1349 gpa: Allocator,
1350 addrs: *ArrayList(LookupAddr),
1351 canon: *ArrayList(u8),
13461352 name: []const u8,
13471353 family: posix.sa_family_t,
13481354 port: u16,
......@@ -1359,7 +1365,34 @@ fn linuxLookupNameFromHosts(
13591365 var line_buf: [512]u8 = undefined;
13601366 var file_reader = file.reader();
13611367 var br = file_reader.interface().buffered(&line_buf);
1362 while (br.takeSentinel('\n')) |line| {
1368 return parseHosts(gpa, addrs, canon, name, family, port, &br) catch |err| switch (err) {
1369 error.OutOfMemory => return error.OutOfMemory,
1370 error.ReadFailed => return file_reader.err.?,
1371 };
1372}
1373
1374fn parseHosts(
1375 gpa: Allocator,
1376 addrs: *ArrayList(LookupAddr),
1377 canon: *ArrayList(u8),
1378 name: []const u8,
1379 family: posix.sa_family_t,
1380 port: u16,
1381 br: *std.io.BufferedReader,
1382) error{ OutOfMemory, ReadFailed }!void {
1383 while (true) {
1384 const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) {
1385 error.StreamTooLong => {
1386 // Skip lines that are too long.
1387 br.discardDelimiterInclusive('\n') catch |e| switch (e) {
1388 error.EndOfStream => break,
1389 error.ReadFailed => return error.ReadFailed,
1390 };
1391 continue;
1392 },
1393 error.ReadFailed => return error.ReadFailed,
1394 error.EndOfStream => break,
1395 };
13631396 var split_it = mem.splitScalar(u8, line, '#');
13641397 const no_comment_line = split_it.first();
13651398
......@@ -1383,15 +1416,15 @@ fn linuxLookupNameFromHosts(
13831416 error.NonCanonical,
13841417 => continue,
13851418 };
1386 try addrs.append(LookupAddr{ .addr = addr });
1419 try addrs.append(gpa, .{ .addr = addr });
13871420
13881421 // first name is canonical name
13891422 const name_text = first_name_text.?;
13901423 if (isValidHostName(name_text)) {
13911424 canon.items.len = 0;
1392 try canon.appendSlice(name_text);
1425 try canon.appendSlice(gpa, name_text);
13931426 }
1394 } else |err| return err;
1427 }
13951428}
13961429
13971430pub fn isValidHostName(hostname: []const u8) bool {
......@@ -1407,14 +1440,15 @@ pub fn isValidHostName(hostname: []const u8) bool {
14071440}
14081441
14091442fn linuxLookupNameFromDnsSearch(
1410 addrs: *std.ArrayList(LookupAddr),
1411 canon: *std.ArrayList(u8),
1443 gpa: Allocator,
1444 addrs: *ArrayList(LookupAddr),
1445 canon: *ArrayList(u8),
14121446 name: []const u8,
14131447 family: posix.sa_family_t,
14141448 port: u16,
14151449) !void {
14161450 var rc: ResolvConf = undefined;
1417 try getResolvConf(addrs.allocator, &rc);
1451 rc.init(gpa) catch return error.ResolveConfParseFailed;
14181452 defer rc.deinit();
14191453
14201454 // Count dots, suppress search when >=ndots or name ends in
......@@ -1439,37 +1473,40 @@ fn linuxLookupNameFromDnsSearch(
14391473 // provides the desired default canonical name (if the requested
14401474 // name is not a CNAME record) and serves as a buffer for passing
14411475 // the full requested name to name_from_dns.
1442 try canon.resize(canon_name.len);
1476 try canon.resize(gpa, canon_name.len);
14431477 @memcpy(canon.items, canon_name);
1444 try canon.append('.');
1478 try canon.append(gpa, '.');
14451479
14461480 var tok_it = mem.tokenizeAny(u8, search, " \t");
14471481 while (tok_it.next()) |tok| {
14481482 canon.shrinkRetainingCapacity(canon_name.len + 1);
1449 try canon.appendSlice(tok);
1450 try linuxLookupNameFromDns(addrs, canon, canon.items, family, rc, port);
1483 try canon.appendSlice(gpa, tok);
1484 try linuxLookupNameFromDns(gpa, addrs, canon, canon.items, family, rc, port);
14511485 if (addrs.items.len != 0) return;
14521486 }
14531487
14541488 canon.shrinkRetainingCapacity(canon_name.len);
1455 return linuxLookupNameFromDns(addrs, canon, name, family, rc, port);
1489 return linuxLookupNameFromDns(gpa, addrs, canon, name, family, rc, port);
14561490}
14571491
14581492const dpc_ctx = struct {
1459 addrs: *std.ArrayList(LookupAddr),
1460 canon: *std.ArrayList(u8),
1493 gpa: Allocator,
1494 addrs: *ArrayList(LookupAddr),
1495 canon: *ArrayList(u8),
14611496 port: u16,
14621497};
14631498
14641499fn linuxLookupNameFromDns(
1465 addrs: *std.ArrayList(LookupAddr),
1466 canon: *std.ArrayList(u8),
1500 gpa: Allocator,
1501 addrs: *ArrayList(LookupAddr),
1502 canon: *ArrayList(u8),
14671503 name: []const u8,
14681504 family: posix.sa_family_t,
14691505 rc: ResolvConf,
14701506 port: u16,
14711507) !void {
1472 const ctx = dpc_ctx{
1508 const ctx: dpc_ctx = .{
1509 .gpa = gpa,
14731510 .addrs = addrs,
14741511 .canon = canon,
14751512 .port = port,
......@@ -1479,8 +1516,8 @@ fn linuxLookupNameFromDns(
14791516 rr: u8,
14801517 };
14811518 const afrrs = [_]AfRr{
1482 AfRr{ .af = posix.AF.INET6, .rr = posix.RR.A },
1483 AfRr{ .af = posix.AF.INET, .rr = posix.RR.AAAA },
1519 .{ .af = posix.AF.INET6, .rr = posix.RR.A },
1520 .{ .af = posix.AF.INET, .rr = posix.RR.AAAA },
14841521 };
14851522 var qbuf: [2][280]u8 = undefined;
14861523 var abuf: [2][512]u8 = undefined;
......@@ -1500,7 +1537,7 @@ fn linuxLookupNameFromDns(
15001537 ap[0].len = 0;
15011538 ap[1].len = 0;
15021539
1503 try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc);
1540 try rc.resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq]);
15041541
15051542 var i: usize = 0;
15061543 while (i < nq) : (i += 1) {
......@@ -1515,240 +1552,252 @@ fn linuxLookupNameFromDns(
15151552}
15161553
15171554const ResolvConf = struct {
1555 gpa: Allocator,
15181556 attempts: u32,
15191557 ndots: u32,
15201558 timeout: u32,
1521 search: std.ArrayList(u8),
1522 ns: std.ArrayList(LookupAddr),
1523
1524 fn deinit(rc: *ResolvConf) void {
1525 rc.ns.deinit();
1526 rc.search.deinit();
1527 rc.* = undefined;
1559 search: ArrayList(u8),
1560 /// TODO there are actually only allowed to be maximum 3 nameservers, no need
1561 /// for an array list.
1562 ns: ArrayList(LookupAddr),
1563
1564 /// Returns `error.StreamTooLong` if a line is longer than 512 bytes.
1565 /// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761
1566 fn init(rc: *ResolvConf, gpa: Allocator) !void {
1567 rc.* = .{
1568 .gpa = gpa,
1569 .ns = .empty,
1570 .search = .empty,
1571 .ndots = 1,
1572 .timeout = 5,
1573 .attempts = 2,
1574 };
1575 errdefer rc.deinit();
1576
1577 const file = fs.openFileAbsoluteZ("/etc/resolv.conf", .{}) catch |err| switch (err) {
1578 error.FileNotFound,
1579 error.NotDir,
1580 error.AccessDenied,
1581 => return linuxLookupNameFromNumericUnspec(gpa, &rc.ns, "127.0.0.1", 53),
1582 else => |e| return e,
1583 };
1584 defer file.close();
1585
1586 var line_buf: [512]u8 = undefined;
1587 var file_reader = file.reader();
1588 var br = file_reader.interface().buffered(&line_buf);
1589 return parse(rc, &br) catch |err| switch (err) {
1590 error.ReadFailed => return file_reader.err.?,
1591 else => |e| return e,
1592 };
15281593 }
1529};
1530
1531/// Returns `error.StreamTooLong` if a line is longer than 512 bytes.
1532/// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761
1533fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void {
1534 rc.* = .{
1535 .ns = std.ArrayList(LookupAddr).init(allocator),
1536 .search = std.ArrayList(u8).init(allocator),
1537 .ndots = 1,
1538 .timeout = 5,
1539 .attempts = 2,
1540 };
1541 errdefer rc.deinit();
1542
1543 const file = fs.openFileAbsoluteZ("/etc/resolv.conf", .{}) catch |err| switch (err) {
1544 error.FileNotFound,
1545 error.NotDir,
1546 error.AccessDenied,
1547 => return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53),
1548 else => |e| return e,
1549 };
1550 defer file.close();
15511594
1552 var line_buf: [512]u8 = undefined;
1553 var file_reader = file.reader();
1554 var br = file_reader.interface().buffered(&line_buf);
1555 while (br.takeSentinel('\n')) |line_with_comment| {
1556 const line = line: {
1557 var split = mem.splitScalar(u8, line_with_comment, '#');
1558 break :line split.first();
1559 };
1560 var line_it = mem.tokenizeAny(u8, line, " \t");
1561
1562 const token = line_it.next() orelse continue;
1563 if (mem.eql(u8, token, "options")) {
1564 while (line_it.next()) |sub_tok| {
1565 var colon_it = mem.splitScalar(u8, sub_tok, ':');
1566 const name = colon_it.first();
1567 const value_txt = colon_it.next() orelse continue;
1568 const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) {
1569 // TODO https://github.com/ziglang/zig/issues/11812
1570 error.Overflow => @as(u8, 255),
1571 error.InvalidCharacter => continue,
1572 };
1573 if (mem.eql(u8, name, "ndots")) {
1574 rc.ndots = @min(value, 15);
1575 } else if (mem.eql(u8, name, "attempts")) {
1576 rc.attempts = @min(value, 10);
1577 } else if (mem.eql(u8, name, "timeout")) {
1578 rc.timeout = @min(value, 60);
1595 fn parse(rc: *ResolvConf, br: *std.io.BufferedReader) !void {
1596 const gpa = rc.gpa;
1597 while (br.takeSentinel('\n')) |line_with_comment| {
1598 const line = line: {
1599 var split = mem.splitScalar(u8, line_with_comment, '#');
1600 break :line split.first();
1601 };
1602 var line_it = mem.tokenizeAny(u8, line, " \t");
1603
1604 const token = line_it.next() orelse continue;
1605 if (mem.eql(u8, token, "options")) {
1606 while (line_it.next()) |sub_tok| {
1607 var colon_it = mem.splitScalar(u8, sub_tok, ':');
1608 const name = colon_it.first();
1609 const value_txt = colon_it.next() orelse continue;
1610 const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) {
1611 error.Overflow => 255,
1612 error.InvalidCharacter => continue,
1613 };
1614 if (mem.eql(u8, name, "ndots")) {
1615 rc.ndots = @min(value, 15);
1616 } else if (mem.eql(u8, name, "attempts")) {
1617 rc.attempts = @min(value, 10);
1618 } else if (mem.eql(u8, name, "timeout")) {
1619 rc.timeout = @min(value, 60);
1620 }
15791621 }
1622 } else if (mem.eql(u8, token, "nameserver")) {
1623 const ip_txt = line_it.next() orelse continue;
1624 try linuxLookupNameFromNumericUnspec(gpa, &rc.ns, ip_txt, 53);
1625 } else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) {
1626 rc.search.items.len = 0;
1627 try rc.search.appendSlice(gpa, line_it.rest());
15801628 }
1581 } else if (mem.eql(u8, token, "nameserver")) {
1582 const ip_txt = line_it.next() orelse continue;
1583 try linuxLookupNameFromNumericUnspec(&rc.ns, ip_txt, 53);
1584 } else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) {
1585 rc.search.items.len = 0;
1586 try rc.search.appendSlice(line_it.rest());
1587 }
1588 } else |err| return err;
1629 } else |err| return err;
15891630
1590 if (rc.ns.items.len == 0) {
1591 return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53);
1631 if (rc.ns.items.len == 0) {
1632 return linuxLookupNameFromNumericUnspec(gpa, &rc.ns, "127.0.0.1", 53);
1633 }
15921634 }
1593}
15941635
1595fn linuxLookupNameFromNumericUnspec(
1596 addrs: *std.ArrayList(LookupAddr),
1597 name: []const u8,
1598 port: u16,
1599) !void {
1600 const addr = try Address.resolveIp(name, port);
1601 (try addrs.addOne()).* = LookupAddr{ .addr = addr };
1602}
1603
1604fn resMSendRc(
1605 queries: []const []const u8,
1606 answers: [][]u8,
1607 answer_bufs: []const []u8,
1608 rc: ResolvConf,
1609) !void {
1610 const timeout = 1000 * rc.timeout;
1611 const attempts = rc.attempts;
1636 fn resMSendRc(
1637 rc: ResolvConf,
1638 queries: []const []const u8,
1639 answers: [][]u8,
1640 answer_bufs: []const []u8,
1641 ) !void {
1642 const gpa = rc.gpa;
1643 const timeout = 1000 * rc.timeout;
1644 const attempts = rc.attempts;
16121645
1613 var sl: posix.socklen_t = @sizeOf(posix.sockaddr.in);
1614 var family: posix.sa_family_t = posix.AF.INET;
1646 var sl: posix.socklen_t = @sizeOf(posix.sockaddr.in);
1647 var family: posix.sa_family_t = posix.AF.INET;
16151648
1616 var ns_list = std.ArrayList(Address).init(rc.ns.allocator);
1617 defer ns_list.deinit();
1649 var ns_list: ArrayList(Address) = .empty;
1650 defer ns_list.deinit(gpa);
16181651
1619 try ns_list.resize(rc.ns.items.len);
1620 const ns = ns_list.items;
1652 try ns_list.resize(gpa, rc.ns.items.len);
16211653
1622 for (rc.ns.items, 0..) |iplit, i| {
1623 ns[i] = iplit.addr;
1624 assert(ns[i].getPort() == 53);
1625 if (iplit.addr.any.family != posix.AF.INET) {
1626 family = posix.AF.INET6;
1654 for (ns_list.items, rc.ns.items) |*ns, iplit| {
1655 ns.* = iplit.addr;
1656 assert(ns.getPort() == 53);
1657 if (iplit.addr.any.family != posix.AF.INET) {
1658 family = posix.AF.INET6;
1659 }
16271660 }
1628 }
16291661
1630 const flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC | posix.SOCK.NONBLOCK;
1631 const fd = posix.socket(family, flags, 0) catch |err| switch (err) {
1632 error.AddressFamilyNotSupported => blk: {
1633 // Handle case where system lacks IPv6 support
1634 if (family == posix.AF.INET6) {
1635 family = posix.AF.INET;
1636 break :blk try posix.socket(posix.AF.INET, flags, 0);
1662 const flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC | posix.SOCK.NONBLOCK;
1663 const fd = posix.socket(family, flags, 0) catch |err| switch (err) {
1664 error.AddressFamilyNotSupported => blk: {
1665 // Handle case where system lacks IPv6 support
1666 if (family == posix.AF.INET6) {
1667 family = posix.AF.INET;
1668 break :blk try posix.socket(posix.AF.INET, flags, 0);
1669 }
1670 return err;
1671 },
1672 else => |e| return e,
1673 };
1674 defer Stream.close(.{ .handle = fd });
1675
1676 // Past this point, there are no errors. Each individual query will
1677 // yield either no reply (indicated by zero length) or an answer
1678 // packet which is up to the caller to interpret.
1679
1680 // Convert any IPv4 addresses in a mixed environment to v4-mapped
1681 if (family == posix.AF.INET6) {
1682 try posix.setsockopt(
1683 fd,
1684 posix.SOL.IPV6,
1685 std.os.linux.IPV6.V6ONLY,
1686 &mem.toBytes(@as(c_int, 0)),
1687 );
1688 for (ns_list.items) |*ns| {
1689 if (ns.any.family != posix.AF.INET) continue;
1690 mem.writeInt(u32, ns.in6.sa.addr[12..], ns.in.sa.addr, native_endian);
1691 ns.in6.sa.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
1692 ns.any.family = posix.AF.INET6;
1693 ns.in6.sa.flowinfo = 0;
1694 ns.in6.sa.scope_id = 0;
16371695 }
1638 return err;
1639 },
1640 else => |e| return e,
1641 };
1642 defer Stream.close(.{ .handle = fd });
1643
1644 // Past this point, there are no errors. Each individual query will
1645 // yield either no reply (indicated by zero length) or an answer
1646 // packet which is up to the caller to interpret.
1647
1648 // Convert any IPv4 addresses in a mixed environment to v4-mapped
1649 if (family == posix.AF.INET6) {
1650 try posix.setsockopt(
1651 fd,
1652 posix.SOL.IPV6,
1653 std.os.linux.IPV6.V6ONLY,
1654 &mem.toBytes(@as(c_int, 0)),
1655 );
1656 for (0..ns.len) |i| {
1657 if (ns[i].any.family != posix.AF.INET) continue;
1658 mem.writeInt(u32, ns[i].in6.sa.addr[12..], ns[i].in.sa.addr, native_endian);
1659 ns[i].in6.sa.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
1660 ns[i].any.family = posix.AF.INET6;
1661 ns[i].in6.sa.flowinfo = 0;
1662 ns[i].in6.sa.scope_id = 0;
1696 sl = @sizeOf(posix.sockaddr.in6);
16631697 }
1664 sl = @sizeOf(posix.sockaddr.in6);
1665 }
16661698
1667 // Get local address and open/bind a socket
1668 var sa: Address = undefined;
1669 @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0);
1670 sa.any.family = family;
1671 try posix.bind(fd, &sa.any, sl);
1672
1673 var pfd = [1]posix.pollfd{posix.pollfd{
1674 .fd = fd,
1675 .events = posix.POLL.IN,
1676 .revents = undefined,
1677 }};
1678 const retry_interval = timeout / attempts;
1679 var next: u32 = 0;
1680 var t2: u64 = @bitCast(std.time.milliTimestamp());
1681 const t0 = t2;
1682 var t1 = t2 - retry_interval;
1683
1684 var servfail_retry: usize = undefined;
1685
1686 outer: while (t2 - t0 < timeout) : (t2 = @as(u64, @bitCast(std.time.milliTimestamp()))) {
1687 if (t2 - t1 >= retry_interval) {
1688 // Query all configured nameservers in parallel
1689 var i: usize = 0;
1690 while (i < queries.len) : (i += 1) {
1691 if (answers[i].len == 0) {
1692 var j: usize = 0;
1693 while (j < ns.len) : (j += 1) {
1694 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined;
1699 // Get local address and open/bind a socket
1700 var sa: Address = undefined;
1701 @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0);
1702 sa.any.family = family;
1703 try posix.bind(fd, &sa.any, sl);
1704
1705 var pfd = [1]posix.pollfd{posix.pollfd{
1706 .fd = fd,
1707 .events = posix.POLL.IN,
1708 .revents = undefined,
1709 }};
1710 const retry_interval = timeout / attempts;
1711 var next: u32 = 0;
1712 var t2: u64 = @bitCast(std.time.milliTimestamp());
1713 const t0 = t2;
1714 var t1 = t2 - retry_interval;
1715
1716 var servfail_retry: usize = undefined;
1717
1718 outer: while (t2 - t0 < timeout) : (t2 = @as(u64, @bitCast(std.time.milliTimestamp()))) {
1719 if (t2 - t1 >= retry_interval) {
1720 // Query all configured nameservers in parallel
1721 var i: usize = 0;
1722 while (i < queries.len) : (i += 1) {
1723 if (answers[i].len == 0) {
1724 for (ns_list.items) |*ns| {
1725 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns.any, sl) catch undefined;
1726 }
16951727 }
16961728 }
1729 t1 = t2;
1730 servfail_retry = 2 * queries.len;
16971731 }
1698 t1 = t2;
1699 servfail_retry = 2 * queries.len;
1700 }
17011732
1702 // Wait for a response, or until time to retry
1703 const clamped_timeout = @min(@as(u31, std.math.maxInt(u31)), t1 + retry_interval - t2);
1704 const nevents = posix.poll(&pfd, clamped_timeout) catch 0;
1705 if (nevents == 0) continue;
1733 // Wait for a response, or until time to retry
1734 const clamped_timeout = @min(@as(u31, std.math.maxInt(u31)), t1 + retry_interval - t2);
1735 const nevents = posix.poll(&pfd, clamped_timeout) catch 0;
1736 if (nevents == 0) continue;
1737
1738 while (true) {
1739 var sl_copy = sl;
1740 const rlen = posix.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;
1741
1742 // Ignore non-identifiable packets
1743 if (rlen < 4) continue;
1744
1745 // Ignore replies from addresses we didn't send to
1746 const ns = for (ns_list.items) |*ns| {
1747 if (ns.eql(sa)) break ns;
1748 } else continue;
1749
1750 // Find which query this answer goes with, if any
1751 var i: usize = next;
1752 while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or
1753 answer_bufs[next][1] != queries[i][1])) : (i += 1)
1754 {}
1755
1756 if (i == queries.len) continue;
1757 if (answers[i].len != 0) continue;
1758
1759 // Only accept positive or negative responses;
1760 // retry immediately on server failure, and ignore
1761 // all other codes such as refusal.
1762 switch (answer_bufs[next][3] & 15) {
1763 0, 3 => {},
1764 2 => if (servfail_retry != 0) {
1765 servfail_retry -= 1;
1766 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns.any, sl) catch undefined;
1767 },
1768 else => continue,
1769 }
17061770
1707 while (true) {
1708 var sl_copy = sl;
1709 const rlen = posix.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;
1710
1711 // Ignore non-identifiable packets
1712 if (rlen < 4) continue;
1713
1714 // Ignore replies from addresses we didn't send to
1715 var j: usize = 0;
1716 while (j < ns.len and !ns[j].eql(sa)) : (j += 1) {}
1717 if (j == ns.len) continue;
1718
1719 // Find which query this answer goes with, if any
1720 var i: usize = next;
1721 while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or
1722 answer_bufs[next][1] != queries[i][1])) : (i += 1)
1723 {}
1724
1725 if (i == queries.len) continue;
1726 if (answers[i].len != 0) continue;
1727
1728 // Only accept positive or negative responses;
1729 // retry immediately on server failure, and ignore
1730 // all other codes such as refusal.
1731 switch (answer_bufs[next][3] & 15) {
1732 0, 3 => {},
1733 2 => if (servfail_retry != 0) {
1734 servfail_retry -= 1;
1735 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined;
1736 },
1737 else => continue,
1738 }
1771 // Store answer in the right slot, or update next
1772 // available temp slot if it's already in place.
1773 answers[i].len = rlen;
1774 if (i == next) {
1775 while (next < queries.len and answers[next].len != 0) : (next += 1) {}
1776 } else {
1777 @memcpy(answer_bufs[i][0..rlen], answer_bufs[next][0..rlen]);
1778 }
17391779
1740 // Store answer in the right slot, or update next
1741 // available temp slot if it's already in place.
1742 answers[i].len = rlen;
1743 if (i == next) {
1744 while (next < queries.len and answers[next].len != 0) : (next += 1) {}
1745 } else {
1746 @memcpy(answer_bufs[i][0..rlen], answer_bufs[next][0..rlen]);
1780 if (next == queries.len) break :outer;
17471781 }
1748
1749 if (next == queries.len) break :outer;
17501782 }
17511783 }
1784
1785 fn deinit(rc: *ResolvConf) void {
1786 const gpa = rc.gpa;
1787 rc.ns.deinit(gpa);
1788 rc.search.deinit(gpa);
1789 rc.* = undefined;
1790 }
1791};
1792
1793fn linuxLookupNameFromNumericUnspec(
1794 gpa: Allocator,
1795 addrs: *ArrayList(LookupAddr),
1796 name: []const u8,
1797 port: u16,
1798) !void {
1799 const addr = try Address.resolveIp(name, port);
1800 try addrs.append(gpa, .{ .addr = addr });
17521801}
17531802
17541803fn dnsParse(
......@@ -1785,20 +1834,19 @@ fn dnsParse(
17851834}
17861835
17871836fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void {
1837 const gpa = ctx.gpa;
17881838 switch (rr) {
17891839 posix.RR.A => {
17901840 if (data.len != 4) return error.InvalidDnsARecord;
1791 const new_addr = try ctx.addrs.addOne();
1792 new_addr.* = LookupAddr{
1841 try ctx.addrs.append(gpa, .{
17931842 .addr = Address.initIp4(data[0..4].*, ctx.port),
1794 };
1843 });
17951844 },
17961845 posix.RR.AAAA => {
17971846 if (data.len != 16) return error.InvalidDnsAAAARecord;
1798 const new_addr = try ctx.addrs.addOne();
1799 new_addr.* = LookupAddr{
1847 try ctx.addrs.append(gpa, .{
18001848 .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0),
1801 };
1849 });
18021850 },
18031851 posix.RR.CNAME => {
18041852 var tmp: [256]u8 = undefined;
......@@ -1807,7 +1855,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
18071855 const canon_name = mem.sliceTo(&tmp, 0);
18081856 if (isValidHostName(canon_name)) {
18091857 ctx.canon.items.len = 0;
1810 try ctx.canon.appendSlice(canon_name);
1858 try ctx.canon.appendSlice(gpa, canon_name);
18111859 }
18121860 },
18131861 else => return,