authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-15 01:42:40+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-07-15 01:42:40+02:00
log148befdaa32cb42837f83733b4f299be86743d71
tree363119c1b8430966a2260a16640b932bb1160e17
parentf3f2a56859f96cee6f9bc8e8fe14b99ec653abaf
parente4ebbdb354f5d562f7fca025f57e4775d9c23882
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #24409 from ziglang/net

std.net: update to new I/O API

13 files changed, 1132 insertions(+), 675 deletions(-)

lib/std/Build/Cache.zig+2-1
......@@ -661,7 +661,8 @@ pub const Manifest = struct {
661661 } {
662662 const gpa = self.cache.gpa;
663663 const input_file_count = self.files.entries.len;
664 var manifest_reader = self.manifest_file.?.reader(&.{}); // Reads positionally from zero.
664 var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded
665 var manifest_reader = self.manifest_file.?.reader(&tiny_buffer); // Reads positionally from zero.
665666 const limit: std.io.Limit = .limited(manifest_file_size_max);
666667 const file_contents = manifest_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) {
667668 error.OutOfMemory => return error.OutOfMemory,
lib/std/Io.zig+27-1
......@@ -312,6 +312,32 @@ pub fn GenericReader(
312312 const ptr: *const Context = @alignCast(@ptrCast(context));
313313 return readFn(ptr.*, buffer);
314314 }
315
316 /// Helper for bridging to the new `Reader` API while upgrading.
317 pub fn adaptToNewApi(self: *const Self) Adapter {
318 return .{
319 .derp_reader = self.*,
320 .new_interface = .{
321 .buffer = &.{},
322 .vtable = &.{ .stream = Adapter.stream },
323 },
324 };
325 }
326
327 pub const Adapter = struct {
328 derp_reader: Self,
329 new_interface: Reader,
330 err: ?Error = null,
331
332 fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
333 const a: *@This() = @alignCast(@fieldParentPtr("new_interface", r));
334 const buf = limit.slice(try w.writableSliceGreedy(1));
335 return a.derp_reader.read(buf) catch |err| {
336 a.err = err;
337 return error.ReadFailed;
338 };
339 }
340 };
315341 };
316342}
317343
......@@ -393,7 +419,7 @@ pub fn GenericWriter(
393419
394420 fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
395421 _ = splat;
396 const a: *@This() = @fieldParentPtr("new_interface", w);
422 const a: *@This() = @alignCast(@fieldParentPtr("new_interface", w));
397423 return a.derp_writer.write(data[0]) catch |err| {
398424 a.err = err;
399425 return error.WriteFailed;
lib/std/Io/Reader.zig+3-1
......@@ -245,6 +245,7 @@ pub fn appendRemaining(
245245 list: *std.ArrayListAlignedUnmanaged(u8, alignment),
246246 limit: Limit,
247247) LimitedAllocError!void {
248 assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
248249 const buffer = r.buffer;
249250 const buffer_contents = buffer[r.seek..r.end];
250251 const copy_len = limit.minInt(buffer_contents.len);
......@@ -1657,11 +1658,12 @@ test "readAlloc when the backing reader provides one byte at a time" {
16571658 }
16581659 };
16591660 const str = "This is a test";
1661 var tiny_buffer: [1]u8 = undefined;
16601662 var one_byte_stream: OneByteReader = .{
16611663 .str = str,
16621664 .i = 0,
16631665 .reader = .{
1664 .buffer = &.{},
1666 .buffer = &tiny_buffer,
16651667 .vtable = &.{ .stream = OneByteReader.stream },
16661668 .seek = 0,
16671669 .end = 0,
lib/std/Io/Writer.zig+27-1
......@@ -393,6 +393,32 @@ pub fn writableVectorPosix(w: *Writer, buffer: []std.posix.iovec, limit: Limit)
393393 return buffer[0..i];
394394}
395395
396pub fn writableVectorWsa(
397 w: *Writer,
398 buffer: []std.os.windows.ws2_32.WSABUF,
399 limit: Limit,
400) Error![]std.os.windows.ws2_32.WSABUF {
401 var it = try writableVectorIterator(w);
402 var i: usize = 0;
403 var remaining = limit;
404 while (it.next()) |full_buffer| {
405 if (!remaining.nonzero()) break;
406 if (buffer.len - i == 0) break;
407 const buf = remaining.slice(full_buffer);
408 if (buf.len == 0) continue;
409 if (std.math.cast(u32, buf.len)) |len| {
410 buffer[i] = .{ .buf = buf.ptr, .len = len };
411 i += 1;
412 remaining = remaining.subtract(len).?;
413 continue;
414 }
415 buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
416 i += 1;
417 break;
418 }
419 return buffer[0..i];
420}
421
396422pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {
397423 _ = try writableSliceGreedy(w, n);
398424}
......@@ -2162,7 +2188,7 @@ pub const Discarding = struct {
21622188 pub fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
21632189 const d: *Discarding = @alignCast(@fieldParentPtr("writer", w));
21642190 const slice = data[0 .. data.len - 1];
2165 const pattern = data[slice.len..];
2191 const pattern = data[slice.len];
21662192 var written: usize = pattern.len * splat;
21672193 for (slice) |bytes| written += bytes.len;
21682194 d.count += w.end + written;
lib/std/c.zig+39-2
......@@ -4110,7 +4110,7 @@ pub const msghdr_const = switch (native_os) {
41104110 /// scatter/gather array
41114111 iov: [*]const iovec_const,
41124112 /// # elements in iov
4113 iovlen: i32,
4113 iovlen: u32,
41144114 /// ancillary data
41154115 control: ?*const anyopaque,
41164116 /// ancillary data buffer len
......@@ -4122,7 +4122,7 @@ pub const msghdr_const = switch (native_os) {
41224122 name: ?*const anyopaque,
41234123 namelen: socklen_t,
41244124 iov: [*]const iovec,
4125 iovlen: c_int,
4125 iovlen: c_uint,
41264126 control: ?*const anyopaque,
41274127 controllen: socklen_t,
41284128 flags: c_int,
......@@ -5625,6 +5625,43 @@ pub const MSG = switch (native_os) {
56255625 pub const NOSIGNAL = 0x80;
56265626 pub const EOR = 0x100;
56275627 },
5628 .freebsd => struct {
5629 pub const OOB = 0x00000001;
5630 pub const PEEK = 0x00000002;
5631 pub const DONTROUTE = 0x00000004;
5632 pub const EOR = 0x00000008;
5633 pub const TRUNC = 0x00000010;
5634 pub const CTRUNC = 0x00000020;
5635 pub const WAITALL = 0x00000040;
5636 pub const DONTWAIT = 0x00000080;
5637 pub const EOF = 0x00000100;
5638 pub const NOTIFICATION = 0x00002000;
5639 pub const NBIO = 0x00004000;
5640 pub const COMPAT = 0x00008000;
5641 pub const SOCALLBCK = 0x00010000;
5642 pub const NOSIGNAL = 0x00020000;
5643 pub const CMSG_CLOEXEC = 0x00040000;
5644 pub const WAITFORONE = 0x00080000;
5645 pub const MORETOCOME = 0x00100000;
5646 pub const TLSAPPDATA = 0x00200000;
5647 },
5648 .netbsd => struct {
5649 pub const OOB = 0x0001;
5650 pub const PEEK = 0x0002;
5651 pub const DONTROUTE = 0x0004;
5652 pub const EOR = 0x0008;
5653 pub const TRUNC = 0x0010;
5654 pub const CTRUNC = 0x0020;
5655 pub const WAITALL = 0x0040;
5656 pub const DONTWAIT = 0x0080;
5657 pub const BCAST = 0x0100;
5658 pub const MCAST = 0x0200;
5659 pub const NOSIGNAL = 0x0400;
5660 pub const CMSG_CLOEXEC = 0x0800;
5661 pub const NBIO = 0x1000;
5662 pub const WAITFORONE = 0x2000;
5663 pub const NOTIFICATION = 0x4000;
5664 },
56285665 else => void,
56295666};
56305667pub const SOCK = switch (native_os) {
lib/std/crypto.zig+2-4
......@@ -1,6 +1,6 @@
11//! Cryptography.
22
3const root = @import("root");
3const std = @import("std.zig");
44
55pub const timing_safe = @import("crypto/timing_safe.zig");
66
......@@ -118,7 +118,7 @@ pub const hash = struct {
118118 pub const blake2 = @import("crypto/blake2.zig");
119119 pub const Blake3 = @import("crypto/blake3.zig").Blake3;
120120 pub const Md5 = @import("crypto/md5.zig").Md5;
121 pub const Sha1 = @import("crypto/sha1.zig").Sha1;
121 pub const Sha1 = @import("crypto/Sha1.zig");
122122 pub const sha2 = @import("crypto/sha2.zig");
123123 pub const sha3 = @import("crypto/sha3.zig");
124124 pub const composition = @import("crypto/hash_composition.zig");
......@@ -216,8 +216,6 @@ pub const random = @import("crypto/tlcsprng.zig").interface;
216216/// Encoding and decoding
217217pub const codecs = @import("crypto/codecs.zig");
218218
219const std = @import("std.zig");
220
221219pub const errors = @import("crypto/errors.zig");
222220
223221pub const tls = @import("crypto/tls.zig");
lib/std/crypto/Sha1.zig created+306
......@@ -0,0 +1,306 @@
1//! The SHA-1 function is now considered cryptographically broken.
2//! Namely, it is feasible to find multiple inputs producing the same hash.
3//! For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3.
4
5const std = @import("../std.zig");
6const mem = std.mem;
7const math = std.math;
8const Sha1 = @This();
9
10pub const block_length = 64;
11pub const digest_length = 20;
12pub const Options = struct {};
13
14s: [5]u32,
15/// Streaming Cache
16buf: [64]u8 = undefined,
17buf_len: u8 = 0,
18total_len: u64 = 0,
19
20pub fn init(options: Options) Sha1 {
21 _ = options;
22 return .{
23 .s = [_]u32{
24 0x67452301,
25 0xEFCDAB89,
26 0x98BADCFE,
27 0x10325476,
28 0xC3D2E1F0,
29 },
30 };
31}
32
33pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
34 var d = Sha1.init(options);
35 d.update(b);
36 d.final(out);
37}
38
39pub fn update(d: *Sha1, b: []const u8) void {
40 var off: usize = 0;
41
42 // Partial buffer exists from previous update. Copy into buffer then hash.
43 if (d.buf_len != 0 and d.buf_len + b.len >= 64) {
44 off += 64 - d.buf_len;
45 @memcpy(d.buf[d.buf_len..][0..off], b[0..off]);
46
47 d.round(d.buf[0..]);
48 d.buf_len = 0;
49 }
50
51 // Full middle blocks.
52 while (off + 64 <= b.len) : (off += 64) {
53 d.round(b[off..][0..64]);
54 }
55
56 // Copy any remainder for next pass.
57 @memcpy(d.buf[d.buf_len..][0 .. b.len - off], b[off..]);
58 d.buf_len += @as(u8, @intCast(b[off..].len));
59
60 d.total_len += b.len;
61}
62
63pub fn peek(d: Sha1) [digest_length]u8 {
64 var copy = d;
65 return copy.finalResult();
66}
67
68pub fn final(d: *Sha1, out: *[digest_length]u8) void {
69 // The buffer here will never be completely full.
70 @memset(d.buf[d.buf_len..], 0);
71
72 // Append padding bits.
73 d.buf[d.buf_len] = 0x80;
74 d.buf_len += 1;
75
76 // > 448 mod 512 so need to add an extra round to wrap around.
77 if (64 - d.buf_len < 8) {
78 d.round(d.buf[0..]);
79 @memset(d.buf[0..], 0);
80 }
81
82 // Append message length.
83 var i: usize = 1;
84 var len = d.total_len >> 5;
85 d.buf[63] = @as(u8, @intCast(d.total_len & 0x1f)) << 3;
86 while (i < 8) : (i += 1) {
87 d.buf[63 - i] = @as(u8, @intCast(len & 0xff));
88 len >>= 8;
89 }
90
91 d.round(d.buf[0..]);
92
93 for (d.s, 0..) |s, j| {
94 mem.writeInt(u32, out[4 * j ..][0..4], s, .big);
95 }
96}
97
98pub fn finalResult(d: *Sha1) [digest_length]u8 {
99 var result: [digest_length]u8 = undefined;
100 d.final(&result);
101 return result;
102}
103
104fn round(d: *Sha1, b: *const [64]u8) void {
105 var s: [16]u32 = undefined;
106
107 var v: [5]u32 = [_]u32{
108 d.s[0],
109 d.s[1],
110 d.s[2],
111 d.s[3],
112 d.s[4],
113 };
114
115 const round0a = comptime [_]RoundParam{
116 .abcdei(0, 1, 2, 3, 4, 0),
117 .abcdei(4, 0, 1, 2, 3, 1),
118 .abcdei(3, 4, 0, 1, 2, 2),
119 .abcdei(2, 3, 4, 0, 1, 3),
120 .abcdei(1, 2, 3, 4, 0, 4),
121 .abcdei(0, 1, 2, 3, 4, 5),
122 .abcdei(4, 0, 1, 2, 3, 6),
123 .abcdei(3, 4, 0, 1, 2, 7),
124 .abcdei(2, 3, 4, 0, 1, 8),
125 .abcdei(1, 2, 3, 4, 0, 9),
126 .abcdei(0, 1, 2, 3, 4, 10),
127 .abcdei(4, 0, 1, 2, 3, 11),
128 .abcdei(3, 4, 0, 1, 2, 12),
129 .abcdei(2, 3, 4, 0, 1, 13),
130 .abcdei(1, 2, 3, 4, 0, 14),
131 .abcdei(0, 1, 2, 3, 4, 15),
132 };
133 inline for (round0a) |r| {
134 s[r.i] = mem.readInt(u32, b[r.i * 4 ..][0..4], .big);
135
136 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
137 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
138 }
139
140 const round0b = comptime [_]RoundParam{
141 .abcdei(4, 0, 1, 2, 3, 16),
142 .abcdei(3, 4, 0, 1, 2, 17),
143 .abcdei(2, 3, 4, 0, 1, 18),
144 .abcdei(1, 2, 3, 4, 0, 19),
145 };
146 inline for (round0b) |r| {
147 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
148 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
149
150 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
151 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
152 }
153
154 const round1 = comptime [_]RoundParam{
155 .abcdei(0, 1, 2, 3, 4, 20),
156 .abcdei(4, 0, 1, 2, 3, 21),
157 .abcdei(3, 4, 0, 1, 2, 22),
158 .abcdei(2, 3, 4, 0, 1, 23),
159 .abcdei(1, 2, 3, 4, 0, 24),
160 .abcdei(0, 1, 2, 3, 4, 25),
161 .abcdei(4, 0, 1, 2, 3, 26),
162 .abcdei(3, 4, 0, 1, 2, 27),
163 .abcdei(2, 3, 4, 0, 1, 28),
164 .abcdei(1, 2, 3, 4, 0, 29),
165 .abcdei(0, 1, 2, 3, 4, 30),
166 .abcdei(4, 0, 1, 2, 3, 31),
167 .abcdei(3, 4, 0, 1, 2, 32),
168 .abcdei(2, 3, 4, 0, 1, 33),
169 .abcdei(1, 2, 3, 4, 0, 34),
170 .abcdei(0, 1, 2, 3, 4, 35),
171 .abcdei(4, 0, 1, 2, 3, 36),
172 .abcdei(3, 4, 0, 1, 2, 37),
173 .abcdei(2, 3, 4, 0, 1, 38),
174 .abcdei(1, 2, 3, 4, 0, 39),
175 };
176 inline for (round1) |r| {
177 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
178 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
179
180 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]);
181 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
182 }
183
184 const round2 = comptime [_]RoundParam{
185 .abcdei(0, 1, 2, 3, 4, 40),
186 .abcdei(4, 0, 1, 2, 3, 41),
187 .abcdei(3, 4, 0, 1, 2, 42),
188 .abcdei(2, 3, 4, 0, 1, 43),
189 .abcdei(1, 2, 3, 4, 0, 44),
190 .abcdei(0, 1, 2, 3, 4, 45),
191 .abcdei(4, 0, 1, 2, 3, 46),
192 .abcdei(3, 4, 0, 1, 2, 47),
193 .abcdei(2, 3, 4, 0, 1, 48),
194 .abcdei(1, 2, 3, 4, 0, 49),
195 .abcdei(0, 1, 2, 3, 4, 50),
196 .abcdei(4, 0, 1, 2, 3, 51),
197 .abcdei(3, 4, 0, 1, 2, 52),
198 .abcdei(2, 3, 4, 0, 1, 53),
199 .abcdei(1, 2, 3, 4, 0, 54),
200 .abcdei(0, 1, 2, 3, 4, 55),
201 .abcdei(4, 0, 1, 2, 3, 56),
202 .abcdei(3, 4, 0, 1, 2, 57),
203 .abcdei(2, 3, 4, 0, 1, 58),
204 .abcdei(1, 2, 3, 4, 0, 59),
205 };
206 inline for (round2) |r| {
207 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
208 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
209
210 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x8F1BBCDC +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d]));
211 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
212 }
213
214 const round3 = comptime [_]RoundParam{
215 .abcdei(0, 1, 2, 3, 4, 60),
216 .abcdei(4, 0, 1, 2, 3, 61),
217 .abcdei(3, 4, 0, 1, 2, 62),
218 .abcdei(2, 3, 4, 0, 1, 63),
219 .abcdei(1, 2, 3, 4, 0, 64),
220 .abcdei(0, 1, 2, 3, 4, 65),
221 .abcdei(4, 0, 1, 2, 3, 66),
222 .abcdei(3, 4, 0, 1, 2, 67),
223 .abcdei(2, 3, 4, 0, 1, 68),
224 .abcdei(1, 2, 3, 4, 0, 69),
225 .abcdei(0, 1, 2, 3, 4, 70),
226 .abcdei(4, 0, 1, 2, 3, 71),
227 .abcdei(3, 4, 0, 1, 2, 72),
228 .abcdei(2, 3, 4, 0, 1, 73),
229 .abcdei(1, 2, 3, 4, 0, 74),
230 .abcdei(0, 1, 2, 3, 4, 75),
231 .abcdei(4, 0, 1, 2, 3, 76),
232 .abcdei(3, 4, 0, 1, 2, 77),
233 .abcdei(2, 3, 4, 0, 1, 78),
234 .abcdei(1, 2, 3, 4, 0, 79),
235 };
236 inline for (round3) |r| {
237 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
238 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
239
240 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0xCA62C1D6 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]);
241 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
242 }
243
244 d.s[0] +%= v[0];
245 d.s[1] +%= v[1];
246 d.s[2] +%= v[2];
247 d.s[3] +%= v[3];
248 d.s[4] +%= v[4];
249}
250
251const RoundParam = struct {
252 a: usize,
253 b: usize,
254 c: usize,
255 d: usize,
256 e: usize,
257 i: u32,
258
259 fn abcdei(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {
260 return .{
261 .a = a,
262 .b = b,
263 .c = c,
264 .d = d,
265 .e = e,
266 .i = i,
267 };
268 }
269};
270
271const htest = @import("test.zig");
272
273test "sha1 single" {
274 try htest.assertEqualHash(Sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", "");
275 try htest.assertEqualHash(Sha1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc");
276 try htest.assertEqualHash(Sha1, "a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
277}
278
279test "sha1 streaming" {
280 var h = Sha1.init(.{});
281 var out: [20]u8 = undefined;
282
283 h.final(&out);
284 try htest.assertEqual("da39a3ee5e6b4b0d3255bfef95601890afd80709", out[0..]);
285
286 h = Sha1.init(.{});
287 h.update("abc");
288 h.final(&out);
289 try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
290
291 h = Sha1.init(.{});
292 h.update("a");
293 h.update("b");
294 h.update("c");
295 h.final(&out);
296 try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
297}
298
299test "sha1 aligned final" {
300 var block = [_]u8{0} ** Sha1.block_length;
301 var out: [Sha1.digest_length]u8 = undefined;
302
303 var h = Sha1.init(.{});
304 h.update(&block);
305 h.final(out[0..]);
306}
lib/std/crypto/sha1.zig deleted-319
......@@ -1,319 +0,0 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const math = std.math;
4
5const RoundParam = struct {
6 a: usize,
7 b: usize,
8 c: usize,
9 d: usize,
10 e: usize,
11 i: u32,
12};
13
14fn roundParam(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {
15 return RoundParam{
16 .a = a,
17 .b = b,
18 .c = c,
19 .d = d,
20 .e = e,
21 .i = i,
22 };
23}
24
25/// The SHA-1 function is now considered cryptographically broken.
26/// Namely, it is feasible to find multiple inputs producing the same hash.
27/// For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3.
28pub const Sha1 = struct {
29 const Self = @This();
30 pub const block_length = 64;
31 pub const digest_length = 20;
32 pub const Options = struct {};
33
34 s: [5]u32,
35 // Streaming Cache
36 buf: [64]u8 = undefined,
37 buf_len: u8 = 0,
38 total_len: u64 = 0,
39
40 pub fn init(options: Options) Self {
41 _ = options;
42 return Self{
43 .s = [_]u32{
44 0x67452301,
45 0xEFCDAB89,
46 0x98BADCFE,
47 0x10325476,
48 0xC3D2E1F0,
49 },
50 };
51 }
52
53 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
54 var d = Sha1.init(options);
55 d.update(b);
56 d.final(out);
57 }
58
59 pub fn update(d: *Self, b: []const u8) void {
60 var off: usize = 0;
61
62 // Partial buffer exists from previous update. Copy into buffer then hash.
63 if (d.buf_len != 0 and d.buf_len + b.len >= 64) {
64 off += 64 - d.buf_len;
65 @memcpy(d.buf[d.buf_len..][0..off], b[0..off]);
66
67 d.round(d.buf[0..]);
68 d.buf_len = 0;
69 }
70
71 // Full middle blocks.
72 while (off + 64 <= b.len) : (off += 64) {
73 d.round(b[off..][0..64]);
74 }
75
76 // Copy any remainder for next pass.
77 @memcpy(d.buf[d.buf_len..][0 .. b.len - off], b[off..]);
78 d.buf_len += @as(u8, @intCast(b[off..].len));
79
80 d.total_len += b.len;
81 }
82
83 pub fn peek(d: Self) [digest_length]u8 {
84 var copy = d;
85 return copy.finalResult();
86 }
87
88 pub fn final(d: *Self, out: *[digest_length]u8) void {
89 // The buffer here will never be completely full.
90 @memset(d.buf[d.buf_len..], 0);
91
92 // Append padding bits.
93 d.buf[d.buf_len] = 0x80;
94 d.buf_len += 1;
95
96 // > 448 mod 512 so need to add an extra round to wrap around.
97 if (64 - d.buf_len < 8) {
98 d.round(d.buf[0..]);
99 @memset(d.buf[0..], 0);
100 }
101
102 // Append message length.
103 var i: usize = 1;
104 var len = d.total_len >> 5;
105 d.buf[63] = @as(u8, @intCast(d.total_len & 0x1f)) << 3;
106 while (i < 8) : (i += 1) {
107 d.buf[63 - i] = @as(u8, @intCast(len & 0xff));
108 len >>= 8;
109 }
110
111 d.round(d.buf[0..]);
112
113 for (d.s, 0..) |s, j| {
114 mem.writeInt(u32, out[4 * j ..][0..4], s, .big);
115 }
116 }
117
118 pub fn finalResult(d: *Self) [digest_length]u8 {
119 var result: [digest_length]u8 = undefined;
120 d.final(&result);
121 return result;
122 }
123
124 fn round(d: *Self, b: *const [64]u8) void {
125 var s: [16]u32 = undefined;
126
127 var v: [5]u32 = [_]u32{
128 d.s[0],
129 d.s[1],
130 d.s[2],
131 d.s[3],
132 d.s[4],
133 };
134
135 const round0a = comptime [_]RoundParam{
136 roundParam(0, 1, 2, 3, 4, 0),
137 roundParam(4, 0, 1, 2, 3, 1),
138 roundParam(3, 4, 0, 1, 2, 2),
139 roundParam(2, 3, 4, 0, 1, 3),
140 roundParam(1, 2, 3, 4, 0, 4),
141 roundParam(0, 1, 2, 3, 4, 5),
142 roundParam(4, 0, 1, 2, 3, 6),
143 roundParam(3, 4, 0, 1, 2, 7),
144 roundParam(2, 3, 4, 0, 1, 8),
145 roundParam(1, 2, 3, 4, 0, 9),
146 roundParam(0, 1, 2, 3, 4, 10),
147 roundParam(4, 0, 1, 2, 3, 11),
148 roundParam(3, 4, 0, 1, 2, 12),
149 roundParam(2, 3, 4, 0, 1, 13),
150 roundParam(1, 2, 3, 4, 0, 14),
151 roundParam(0, 1, 2, 3, 4, 15),
152 };
153 inline for (round0a) |r| {
154 s[r.i] = mem.readInt(u32, b[r.i * 4 ..][0..4], .big);
155
156 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
157 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
158 }
159
160 const round0b = comptime [_]RoundParam{
161 roundParam(4, 0, 1, 2, 3, 16),
162 roundParam(3, 4, 0, 1, 2, 17),
163 roundParam(2, 3, 4, 0, 1, 18),
164 roundParam(1, 2, 3, 4, 0, 19),
165 };
166 inline for (round0b) |r| {
167 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
168 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
169
170 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
171 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
172 }
173
174 const round1 = comptime [_]RoundParam{
175 roundParam(0, 1, 2, 3, 4, 20),
176 roundParam(4, 0, 1, 2, 3, 21),
177 roundParam(3, 4, 0, 1, 2, 22),
178 roundParam(2, 3, 4, 0, 1, 23),
179 roundParam(1, 2, 3, 4, 0, 24),
180 roundParam(0, 1, 2, 3, 4, 25),
181 roundParam(4, 0, 1, 2, 3, 26),
182 roundParam(3, 4, 0, 1, 2, 27),
183 roundParam(2, 3, 4, 0, 1, 28),
184 roundParam(1, 2, 3, 4, 0, 29),
185 roundParam(0, 1, 2, 3, 4, 30),
186 roundParam(4, 0, 1, 2, 3, 31),
187 roundParam(3, 4, 0, 1, 2, 32),
188 roundParam(2, 3, 4, 0, 1, 33),
189 roundParam(1, 2, 3, 4, 0, 34),
190 roundParam(0, 1, 2, 3, 4, 35),
191 roundParam(4, 0, 1, 2, 3, 36),
192 roundParam(3, 4, 0, 1, 2, 37),
193 roundParam(2, 3, 4, 0, 1, 38),
194 roundParam(1, 2, 3, 4, 0, 39),
195 };
196 inline for (round1) |r| {
197 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
198 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
199
200 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]);
201 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
202 }
203
204 const round2 = comptime [_]RoundParam{
205 roundParam(0, 1, 2, 3, 4, 40),
206 roundParam(4, 0, 1, 2, 3, 41),
207 roundParam(3, 4, 0, 1, 2, 42),
208 roundParam(2, 3, 4, 0, 1, 43),
209 roundParam(1, 2, 3, 4, 0, 44),
210 roundParam(0, 1, 2, 3, 4, 45),
211 roundParam(4, 0, 1, 2, 3, 46),
212 roundParam(3, 4, 0, 1, 2, 47),
213 roundParam(2, 3, 4, 0, 1, 48),
214 roundParam(1, 2, 3, 4, 0, 49),
215 roundParam(0, 1, 2, 3, 4, 50),
216 roundParam(4, 0, 1, 2, 3, 51),
217 roundParam(3, 4, 0, 1, 2, 52),
218 roundParam(2, 3, 4, 0, 1, 53),
219 roundParam(1, 2, 3, 4, 0, 54),
220 roundParam(0, 1, 2, 3, 4, 55),
221 roundParam(4, 0, 1, 2, 3, 56),
222 roundParam(3, 4, 0, 1, 2, 57),
223 roundParam(2, 3, 4, 0, 1, 58),
224 roundParam(1, 2, 3, 4, 0, 59),
225 };
226 inline for (round2) |r| {
227 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
228 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
229
230 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0x8F1BBCDC +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d]));
231 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
232 }
233
234 const round3 = comptime [_]RoundParam{
235 roundParam(0, 1, 2, 3, 4, 60),
236 roundParam(4, 0, 1, 2, 3, 61),
237 roundParam(3, 4, 0, 1, 2, 62),
238 roundParam(2, 3, 4, 0, 1, 63),
239 roundParam(1, 2, 3, 4, 0, 64),
240 roundParam(0, 1, 2, 3, 4, 65),
241 roundParam(4, 0, 1, 2, 3, 66),
242 roundParam(3, 4, 0, 1, 2, 67),
243 roundParam(2, 3, 4, 0, 1, 68),
244 roundParam(1, 2, 3, 4, 0, 69),
245 roundParam(0, 1, 2, 3, 4, 70),
246 roundParam(4, 0, 1, 2, 3, 71),
247 roundParam(3, 4, 0, 1, 2, 72),
248 roundParam(2, 3, 4, 0, 1, 73),
249 roundParam(1, 2, 3, 4, 0, 74),
250 roundParam(0, 1, 2, 3, 4, 75),
251 roundParam(4, 0, 1, 2, 3, 76),
252 roundParam(3, 4, 0, 1, 2, 77),
253 roundParam(2, 3, 4, 0, 1, 78),
254 roundParam(1, 2, 3, 4, 0, 79),
255 };
256 inline for (round3) |r| {
257 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
258 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
259
260 v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(u32, 5)) +% 0xCA62C1D6 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]);
261 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
262 }
263
264 d.s[0] +%= v[0];
265 d.s[1] +%= v[1];
266 d.s[2] +%= v[2];
267 d.s[3] +%= v[3];
268 d.s[4] +%= v[4];
269 }
270
271 pub const Error = error{};
272 pub const Writer = std.io.GenericWriter(*Self, Error, write);
273
274 fn write(self: *Self, bytes: []const u8) Error!usize {
275 self.update(bytes);
276 return bytes.len;
277 }
278
279 pub fn writer(self: *Self) Writer {
280 return .{ .context = self };
281 }
282};
283
284const htest = @import("test.zig");
285
286test "sha1 single" {
287 try htest.assertEqualHash(Sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", "");
288 try htest.assertEqualHash(Sha1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc");
289 try htest.assertEqualHash(Sha1, "a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
290}
291
292test "sha1 streaming" {
293 var h = Sha1.init(.{});
294 var out: [20]u8 = undefined;
295
296 h.final(&out);
297 try htest.assertEqual("da39a3ee5e6b4b0d3255bfef95601890afd80709", out[0..]);
298
299 h = Sha1.init(.{});
300 h.update("abc");
301 h.final(&out);
302 try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
303
304 h = Sha1.init(.{});
305 h.update("a");
306 h.update("b");
307 h.update("c");
308 h.final(&out);
309 try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
310}
311
312test "sha1 aligned final" {
313 var block = [_]u8{0} ** Sha1.block_length;
314 var out: [Sha1.digest_length]u8 = undefined;
315
316 var h = Sha1.init(.{});
317 h.update(&block);
318 h.final(out[0..]);
319}
lib/std/fs/File.zig+2-1
......@@ -1369,7 +1369,7 @@ pub const Reader = struct {
13691369 r.pos = offset;
13701370 },
13711371 .streaming, .streaming_reading => {
1372 if (offset >= r.pos) return Reader.seekBy(r, offset - r.pos);
1372 if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - r.pos));
13731373 if (r.seek_err) |err| return err;
13741374 posix.lseek_SET(r.file.handle, offset) catch |err| {
13751375 r.seek_err = err;
......@@ -1657,6 +1657,7 @@ pub const Writer = struct {
16571657 .file = w.file,
16581658 .mode = w.mode,
16591659 .pos = w.pos,
1660 .interface = Reader.initInterface(w.interface.buffer),
16601661 .seek_err = w.seek_err,
16611662 };
16621663 }
lib/std/http/test.zig+15-6
......@@ -135,7 +135,8 @@ test "HTTP server handles a chunked transfer coding request" {
135135 const gpa = std.testing.allocator;
136136 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
137137 defer stream.close();
138 try stream.writeAll(request_bytes);
138 var stream_writer = stream.writer(&.{});
139 try stream_writer.interface.writeAll(request_bytes);
139140
140141 const expected_response =
141142 "HTTP/1.1 200 OK\r\n" ++
......@@ -144,7 +145,9 @@ test "HTTP server handles a chunked transfer coding request" {
144145 "content-type: text/plain\r\n" ++
145146 "\r\n" ++
146147 "message from server!\n";
147 const response = try stream.reader().readAllAlloc(gpa, expected_response.len);
148 var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded
149 var stream_reader = stream.reader(&tiny_buffer);
150 const response = try stream_reader.interface().allocRemaining(gpa, .limited(expected_response.len));
148151 defer gpa.free(response);
149152 try expectEqualStrings(expected_response, response);
150153}
......@@ -276,9 +279,12 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
276279 const gpa = std.testing.allocator;
277280 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
278281 defer stream.close();
279 try stream.writeAll(request_bytes);
282 var stream_writer = stream.writer(&.{});
283 try stream_writer.interface.writeAll(request_bytes);
280284
281 const response = try stream.reader().readAllAlloc(gpa, 8192);
285 var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded
286 var stream_reader = stream.reader(&tiny_buffer);
287 const response = try stream_reader.interface().allocRemaining(gpa, .limited(8192));
282288 defer gpa.free(response);
283289
284290 var expected_response = std.ArrayList(u8).init(gpa);
......@@ -339,9 +345,12 @@ test "receiving arbitrary http headers from the client" {
339345 const gpa = std.testing.allocator;
340346 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
341347 defer stream.close();
342 try stream.writeAll(request_bytes);
348 var stream_writer = stream.writer(&.{});
349 try stream_writer.interface.writeAll(request_bytes);
343350
344 const response = try stream.reader().readAllAlloc(gpa, 8192);
351 var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded
352 var stream_reader = stream.reader(&tiny_buffer);
353 const response = try stream_reader.interface().allocRemaining(gpa, .limited(8192));
345354 defer gpa.free(response);
346355
347356 var expected_response = std.ArrayList(u8).init(gpa);
lib/std/net.zig+697-332
......@@ -11,11 +11,15 @@ 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;
16const File = std.fs.File;
1417
1518// Windows 10 added support for unix sockets in build 17063, redstone 4 is the
1619// first release to support them.
1720pub const has_unix_sockets = switch (native_os) {
1821 .windows => builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false,
22 .wasi => false,
1923 else => true,
2024};
2125
......@@ -719,7 +723,7 @@ pub fn connectUnixSocket(path: []const u8) !Stream {
719723 );
720724 errdefer Stream.close(.{ .handle = sockfd });
721725
722 var addr = try std.net.Address.initUnix(path);
726 var addr = try Address.initUnix(path);
723727 try posix.connect(sockfd, &addr.any, addr.getOsSockLen());
724728
725729 return .{ .handle = sockfd };
......@@ -787,7 +791,7 @@ pub const AddressList = struct {
787791pub const TcpConnectToHostError = GetAddressListError || TcpConnectToAddressError;
788792
789793/// All memory allocated with `allocator` will be freed before this function returns.
790pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream {
794pub fn tcpConnectToHost(allocator: Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream {
791795 const list = try getAddressList(allocator, name, port);
792796 defer list.deinit();
793797
......@@ -818,9 +822,9 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream {
818822 return Stream{ .handle = sockfd };
819823}
820824
821const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{
822 // TODO: break this up into error sets from the various underlying functions
823
825// TODO: Instead of having a massive error set, make the error set have categories, and then
826// store the sub-error as a diagnostic value.
827const GetAddressListError = Allocator.Error || File.OpenError || File.ReadError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{
824828 TemporaryNameServerFailure,
825829 NameServerFailure,
826830 AddressFamilyNotSupported,
......@@ -840,12 +844,13 @@ const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError ||
840844
841845 InterfaceNotFound,
842846 FileSystem,
847 ResolveConfParseFailed,
843848};
844849
845850/// Call `AddressList.deinit` on the result.
846pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList {
851pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList {
847852 const result = blk: {
848 var arena = std.heap.ArenaAllocator.init(allocator);
853 var arena = std.heap.ArenaAllocator.init(gpa);
849854 errdefer arena.deinit();
850855
851856 const result = try arena.allocator().create(AddressList);
......@@ -860,11 +865,11 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
860865 errdefer result.deinit();
861866
862867 if (native_os == .windows) {
863 const name_c = try allocator.dupeZ(u8, name);
864 defer allocator.free(name_c);
868 const name_c = try gpa.dupeZ(u8, name);
869 defer gpa.free(name_c);
865870
866 const port_c = try std.fmt.allocPrintSentinel(allocator, "{}", .{port}, 0);
867 defer allocator.free(port_c);
871 const port_c = try std.fmt.allocPrintSentinel(gpa, "{d}", .{port}, 0);
872 defer gpa.free(port_c);
868873
869874 const ws2_32 = windows.ws2_32;
870875 const hints: posix.addrinfo = .{
......@@ -932,11 +937,11 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
932937 }
933938
934939 if (builtin.link_libc) {
935 const name_c = try allocator.dupeZ(u8, name);
936 defer allocator.free(name_c);
940 const name_c = try gpa.dupeZ(u8, name);
941 defer gpa.free(name_c);
937942
938 const port_c = try std.fmt.allocPrintSentinel(allocator, "{}", .{port}, 0);
939 defer allocator.free(port_c);
943 const port_c = try std.fmt.allocPrintSentinel(gpa, "{d}", .{port}, 0);
944 defer gpa.free(port_c);
940945
941946 const hints: posix.addrinfo = .{
942947 .flags = .{ .NUMERICSERV = true },
......@@ -999,17 +1004,17 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
9991004
10001005 if (native_os == .linux) {
10011006 const family = posix.AF.UNSPEC;
1002 var lookup_addrs = std.ArrayList(LookupAddr).init(allocator);
1003 defer lookup_addrs.deinit();
1007 var lookup_addrs: ArrayList(LookupAddr) = .empty;
1008 defer lookup_addrs.deinit(gpa);
10041009
1005 var canon = std.ArrayList(u8).init(arena);
1006 defer canon.deinit();
1010 var canon: ArrayList(u8) = .empty;
1011 defer canon.deinit(gpa);
10071012
1008 try linuxLookupName(&lookup_addrs, &canon, name, family, .{ .NUMERICSERV = true }, port);
1013 try linuxLookupName(gpa, &lookup_addrs, &canon, name, family, .{ .NUMERICSERV = true }, port);
10091014
10101015 result.addrs = try arena.alloc(Address, lookup_addrs.items.len);
10111016 if (canon.items.len != 0) {
1012 result.canon_name = try canon.toOwnedSlice();
1017 result.canon_name = try arena.dupe(u8, canon.items);
10131018 }
10141019
10151020 for (lookup_addrs.items, 0..) |lookup_addr, i| {
......@@ -1036,8 +1041,9 @@ const DAS_PREFIX_SHIFT = 8;
10361041const DAS_ORDER_SHIFT = 0;
10371042
10381043fn linuxLookupName(
1039 addrs: *std.ArrayList(LookupAddr),
1040 canon: *std.ArrayList(u8),
1044 gpa: Allocator,
1045 addrs: *ArrayList(LookupAddr),
1046 canon: *ArrayList(u8),
10411047 opt_name: ?[]const u8,
10421048 family: posix.sa_family_t,
10431049 flags: posix.AI,
......@@ -1046,13 +1052,13 @@ fn linuxLookupName(
10461052 if (opt_name) |name| {
10471053 // reject empty name and check len so it fits into temp bufs
10481054 canon.items.len = 0;
1049 try canon.appendSlice(name);
1055 try canon.appendSlice(gpa, name);
10501056 if (Address.parseExpectingFamily(name, family, port)) |addr| {
1051 try addrs.append(LookupAddr{ .addr = addr });
1057 try addrs.append(gpa, .{ .addr = addr });
10521058 } else |name_err| if (flags.NUMERICHOST) {
10531059 return name_err;
10541060 } else {
1055 try linuxLookupNameFromHosts(addrs, canon, name, family, port);
1061 try linuxLookupNameFromHosts(gpa, addrs, canon, name, family, port);
10561062 if (addrs.items.len == 0) {
10571063 // RFC 6761 Section 6.3.3
10581064 // Name resolution APIs and libraries SHOULD recognize localhost
......@@ -1063,17 +1069,18 @@ fn linuxLookupName(
10631069 // Check for equal to "localhost(.)" or ends in ".localhost(.)"
10641070 const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost";
10651071 if (mem.endsWith(u8, name, localhost) and (name.len == localhost.len or name[name.len - localhost.len] == '.')) {
1066 try addrs.append(LookupAddr{ .addr = .{ .in = Ip4Address.parse("127.0.0.1", port) catch unreachable } });
1067 try addrs.append(LookupAddr{ .addr = .{ .in6 = Ip6Address.parse("::1", port) catch unreachable } });
1072 try addrs.append(gpa, .{ .addr = .{ .in = Ip4Address.parse("127.0.0.1", port) catch unreachable } });
1073 try addrs.append(gpa, .{ .addr = .{ .in6 = Ip6Address.parse("::1", port) catch unreachable } });
10681074 return;
10691075 }
10701076
1071 try linuxLookupNameFromDnsSearch(addrs, canon, name, family, port);
1077 try linuxLookupNameFromDnsSearch(gpa, addrs, canon, name, family, port);
10721078 }
10731079 }
10741080 } else {
1075 try canon.resize(0);
1076 try linuxLookupNameFromNull(addrs, family, flags, port);
1081 try canon.resize(gpa, 0);
1082 try addrs.ensureUnusedCapacity(gpa, 2);
1083 linuxLookupNameFromNull(addrs, family, flags, port);
10771084 }
10781085 if (addrs.items.len == 0) return error.UnknownHostName;
10791086
......@@ -1279,39 +1286,40 @@ fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool {
12791286}
12801287
12811288fn linuxLookupNameFromNull(
1282 addrs: *std.ArrayList(LookupAddr),
1289 addrs: *ArrayList(LookupAddr),
12831290 family: posix.sa_family_t,
12841291 flags: posix.AI,
12851292 port: u16,
1286) !void {
1293) void {
12871294 if (flags.PASSIVE) {
12881295 if (family != posix.AF.INET6) {
1289 (try addrs.addOne()).* = LookupAddr{
1296 addrs.appendAssumeCapacity(.{
12901297 .addr = Address.initIp4([1]u8{0} ** 4, port),
1291 };
1298 });
12921299 }
12931300 if (family != posix.AF.INET) {
1294 (try addrs.addOne()).* = LookupAddr{
1301 addrs.appendAssumeCapacity(.{
12951302 .addr = Address.initIp6([1]u8{0} ** 16, port, 0, 0),
1296 };
1303 });
12971304 }
12981305 } else {
12991306 if (family != posix.AF.INET6) {
1300 (try addrs.addOne()).* = LookupAddr{
1307 addrs.appendAssumeCapacity(.{
13011308 .addr = Address.initIp4([4]u8{ 127, 0, 0, 1 }, port),
1302 };
1309 });
13031310 }
13041311 if (family != posix.AF.INET) {
1305 (try addrs.addOne()).* = LookupAddr{
1312 addrs.appendAssumeCapacity(.{
13061313 .addr = Address.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0),
1307 };
1314 });
13081315 }
13091316 }
13101317}
13111318
13121319fn linuxLookupNameFromHosts(
1313 addrs: *std.ArrayList(LookupAddr),
1314 canon: *std.ArrayList(u8),
1320 gpa: Allocator,
1321 addrs: *ArrayList(LookupAddr),
1322 canon: *ArrayList(u8),
13151323 name: []const u8,
13161324 family: posix.sa_family_t,
13171325 port: u16,
......@@ -1325,18 +1333,36 @@ fn linuxLookupNameFromHosts(
13251333 };
13261334 defer file.close();
13271335
1328 var buffered_reader = std.io.bufferedReader(file.deprecatedReader());
1329 const reader = buffered_reader.reader();
13301336 var line_buf: [512]u8 = undefined;
1331 while (reader.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
1332 error.StreamTooLong => blk: {
1333 // Skip to the delimiter in the reader, to fix parsing
1334 try reader.skipUntilDelimiterOrEof('\n');
1335 // Use the truncated line. A truncated comment or hostname will be handled correctly.
1336 break :blk &line_buf;
1337 },
1338 else => |e| return e,
1339 }) |line| {
1337 var file_reader = file.reader(&line_buf);
1338 return parseHosts(gpa, addrs, canon, name, family, port, &file_reader.interface) catch |err| switch (err) {
1339 error.OutOfMemory => return error.OutOfMemory,
1340 error.ReadFailed => return file_reader.err.?,
1341 };
1342}
1343
1344fn parseHosts(
1345 gpa: Allocator,
1346 addrs: *ArrayList(LookupAddr),
1347 canon: *ArrayList(u8),
1348 name: []const u8,
1349 family: posix.sa_family_t,
1350 port: u16,
1351 br: *io.Reader,
1352) error{ OutOfMemory, ReadFailed }!void {
1353 while (true) {
1354 const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) {
1355 error.StreamTooLong => {
1356 // Skip lines that are too long.
1357 _ = br.discardDelimiterInclusive('\n') catch |e| switch (e) {
1358 error.EndOfStream => break,
1359 error.ReadFailed => return error.ReadFailed,
1360 };
1361 continue;
1362 },
1363 error.ReadFailed => return error.ReadFailed,
1364 error.EndOfStream => break,
1365 };
13401366 var split_it = mem.splitScalar(u8, line, '#');
13411367 const no_comment_line = split_it.first();
13421368
......@@ -1360,17 +1386,36 @@ fn linuxLookupNameFromHosts(
13601386 error.NonCanonical,
13611387 => continue,
13621388 };
1363 try addrs.append(LookupAddr{ .addr = addr });
1389 try addrs.append(gpa, .{ .addr = addr });
13641390
13651391 // first name is canonical name
13661392 const name_text = first_name_text.?;
13671393 if (isValidHostName(name_text)) {
13681394 canon.items.len = 0;
1369 try canon.appendSlice(name_text);
1395 try canon.appendSlice(gpa, name_text);
13701396 }
13711397 }
13721398}
13731399
1400test parseHosts {
1401 if (builtin.os.tag == .wasi) {
1402 // TODO parsing addresses should not have OS dependencies
1403 return error.SkipZigTest;
1404 }
1405 var reader: std.io.Reader = .fixed(
1406 \\127.0.0.1 localhost
1407 \\::1 localhost
1408 \\127.0.0.2 abcd
1409 );
1410 var addrs: ArrayList(LookupAddr) = .empty;
1411 defer addrs.deinit(std.testing.allocator);
1412 var canon: ArrayList(u8) = .empty;
1413 defer canon.deinit(std.testing.allocator);
1414 try parseHosts(std.testing.allocator, &addrs, &canon, "abcd", posix.AF.UNSPEC, 1234, &reader);
1415 try std.testing.expectEqual(1, addrs.items.len);
1416 try std.testing.expectFmt("127.0.0.2:1234", "{f}", .{addrs.items[0].addr});
1417}
1418
13741419pub fn isValidHostName(hostname: []const u8) bool {
13751420 if (hostname.len >= 254) return false;
13761421 if (!std.unicode.utf8ValidateSlice(hostname)) return false;
......@@ -1384,14 +1429,15 @@ pub fn isValidHostName(hostname: []const u8) bool {
13841429}
13851430
13861431fn linuxLookupNameFromDnsSearch(
1387 addrs: *std.ArrayList(LookupAddr),
1388 canon: *std.ArrayList(u8),
1432 gpa: Allocator,
1433 addrs: *ArrayList(LookupAddr),
1434 canon: *ArrayList(u8),
13891435 name: []const u8,
13901436 family: posix.sa_family_t,
13911437 port: u16,
13921438) !void {
13931439 var rc: ResolvConf = undefined;
1394 try getResolvConf(addrs.allocator, &rc);
1440 rc.init(gpa) catch return error.ResolveConfParseFailed;
13951441 defer rc.deinit();
13961442
13971443 // Count dots, suppress search when >=ndots or name ends in
......@@ -1416,37 +1462,40 @@ fn linuxLookupNameFromDnsSearch(
14161462 // provides the desired default canonical name (if the requested
14171463 // name is not a CNAME record) and serves as a buffer for passing
14181464 // the full requested name to name_from_dns.
1419 try canon.resize(canon_name.len);
1465 try canon.resize(gpa, canon_name.len);
14201466 @memcpy(canon.items, canon_name);
1421 try canon.append('.');
1467 try canon.append(gpa, '.');
14221468
14231469 var tok_it = mem.tokenizeAny(u8, search, " \t");
14241470 while (tok_it.next()) |tok| {
14251471 canon.shrinkRetainingCapacity(canon_name.len + 1);
1426 try canon.appendSlice(tok);
1427 try linuxLookupNameFromDns(addrs, canon, canon.items, family, rc, port);
1472 try canon.appendSlice(gpa, tok);
1473 try linuxLookupNameFromDns(gpa, addrs, canon, canon.items, family, rc, port);
14281474 if (addrs.items.len != 0) return;
14291475 }
14301476
14311477 canon.shrinkRetainingCapacity(canon_name.len);
1432 return linuxLookupNameFromDns(addrs, canon, name, family, rc, port);
1478 return linuxLookupNameFromDns(gpa, addrs, canon, name, family, rc, port);
14331479}
14341480
14351481const dpc_ctx = struct {
1436 addrs: *std.ArrayList(LookupAddr),
1437 canon: *std.ArrayList(u8),
1482 gpa: Allocator,
1483 addrs: *ArrayList(LookupAddr),
1484 canon: *ArrayList(u8),
14381485 port: u16,
14391486};
14401487
14411488fn linuxLookupNameFromDns(
1442 addrs: *std.ArrayList(LookupAddr),
1443 canon: *std.ArrayList(u8),
1489 gpa: Allocator,
1490 addrs: *ArrayList(LookupAddr),
1491 canon: *ArrayList(u8),
14441492 name: []const u8,
14451493 family: posix.sa_family_t,
14461494 rc: ResolvConf,
14471495 port: u16,
14481496) !void {
1449 const ctx = dpc_ctx{
1497 const ctx: dpc_ctx = .{
1498 .gpa = gpa,
14501499 .addrs = addrs,
14511500 .canon = canon,
14521501 .port = port,
......@@ -1456,8 +1505,8 @@ fn linuxLookupNameFromDns(
14561505 rr: u8,
14571506 };
14581507 const afrrs = [_]AfRr{
1459 AfRr{ .af = posix.AF.INET6, .rr = posix.RR.A },
1460 AfRr{ .af = posix.AF.INET, .rr = posix.RR.AAAA },
1508 .{ .af = posix.AF.INET6, .rr = posix.RR.A },
1509 .{ .af = posix.AF.INET, .rr = posix.RR.AAAA },
14611510 };
14621511 var qbuf: [2][280]u8 = undefined;
14631512 var abuf: [2][512]u8 = undefined;
......@@ -1477,7 +1526,7 @@ fn linuxLookupNameFromDns(
14771526 ap[0].len = 0;
14781527 ap[1].len = 0;
14791528
1480 try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc);
1529 try rc.resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq]);
14811530
14821531 var i: usize = 0;
14831532 while (i < nq) : (i += 1) {
......@@ -1492,248 +1541,257 @@ fn linuxLookupNameFromDns(
14921541}
14931542
14941543const ResolvConf = struct {
1544 gpa: Allocator,
14951545 attempts: u32,
14961546 ndots: u32,
14971547 timeout: u32,
1498 search: std.ArrayList(u8),
1499 ns: std.ArrayList(LookupAddr),
1548 search: ArrayList(u8),
1549 /// TODO there are actually only allowed to be maximum 3 nameservers, no need
1550 /// for an array list.
1551 ns: ArrayList(LookupAddr),
1552
1553 /// Returns `error.StreamTooLong` if a line is longer than 512 bytes.
1554 /// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761
1555 fn init(rc: *ResolvConf, gpa: Allocator) !void {
1556 rc.* = .{
1557 .gpa = gpa,
1558 .ns = .empty,
1559 .search = .empty,
1560 .ndots = 1,
1561 .timeout = 5,
1562 .attempts = 2,
1563 };
1564 errdefer rc.deinit();
1565
1566 const file = fs.openFileAbsoluteZ("/etc/resolv.conf", .{}) catch |err| switch (err) {
1567 error.FileNotFound,
1568 error.NotDir,
1569 error.AccessDenied,
1570 => return linuxLookupNameFromNumericUnspec(gpa, &rc.ns, "127.0.0.1", 53),
1571 else => |e| return e,
1572 };
1573 defer file.close();
15001574
1501 fn deinit(rc: *ResolvConf) void {
1502 rc.ns.deinit();
1503 rc.search.deinit();
1504 rc.* = undefined;
1575 var line_buf: [512]u8 = undefined;
1576 var file_reader = file.reader(&line_buf);
1577 return parse(rc, &file_reader.interface) catch |err| switch (err) {
1578 error.ReadFailed => return file_reader.err.?,
1579 else => |e| return e,
1580 };
15051581 }
1506};
1507
1508/// Ignores lines longer than 512 bytes.
1509/// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761
1510fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void {
1511 rc.* = ResolvConf{
1512 .ns = std.ArrayList(LookupAddr).init(allocator),
1513 .search = std.ArrayList(u8).init(allocator),
1514 .ndots = 1,
1515 .timeout = 5,
1516 .attempts = 2,
1517 };
1518 errdefer rc.deinit();
15191582
1520 const file = fs.openFileAbsoluteZ("/etc/resolv.conf", .{}) catch |err| switch (err) {
1521 error.FileNotFound,
1522 error.NotDir,
1523 error.AccessDenied,
1524 => return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53),
1525 else => |e| return e,
1526 };
1527 defer file.close();
1583 const Directive = enum { options, nameserver, domain, search };
1584 const Option = enum { ndots, attempts, timeout };
15281585
1529 var buf_reader = std.io.bufferedReader(file.deprecatedReader());
1530 const stream = buf_reader.reader();
1531 var line_buf: [512]u8 = undefined;
1532 while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
1533 error.StreamTooLong => blk: {
1534 // Skip to the delimiter in the stream, to fix parsing
1535 try stream.skipUntilDelimiterOrEof('\n');
1536 // Give an empty line to the while loop, which will be skipped.
1537 break :blk line_buf[0..0];
1538 },
1539 else => |e| return e,
1540 }) |line| {
1541 const no_comment_line = no_comment_line: {
1542 var split = mem.splitScalar(u8, line, '#');
1543 break :no_comment_line split.first();
1544 };
1545 var line_it = mem.tokenizeAny(u8, no_comment_line, " \t");
1546
1547 const token = line_it.next() orelse continue;
1548 if (mem.eql(u8, token, "options")) {
1549 while (line_it.next()) |sub_tok| {
1550 var colon_it = mem.splitScalar(u8, sub_tok, ':');
1551 const name = colon_it.first();
1552 const value_txt = colon_it.next() orelse continue;
1553 const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) {
1554 // TODO https://github.com/ziglang/zig/issues/11812
1555 error.Overflow => @as(u8, 255),
1556 error.InvalidCharacter => continue,
1557 };
1558 if (mem.eql(u8, name, "ndots")) {
1559 rc.ndots = @min(value, 15);
1560 } else if (mem.eql(u8, name, "attempts")) {
1561 rc.attempts = @min(value, 10);
1562 } else if (mem.eql(u8, name, "timeout")) {
1563 rc.timeout = @min(value, 60);
1564 }
1586 fn parse(rc: *ResolvConf, reader: *io.Reader) !void {
1587 const gpa = rc.gpa;
1588 while (reader.takeSentinel('\n')) |line_with_comment| {
1589 const line = line: {
1590 var split = mem.splitScalar(u8, line_with_comment, '#');
1591 break :line split.first();
1592 };
1593 var line_it = mem.tokenizeAny(u8, line, " \t");
1594
1595 const token = line_it.next() orelse continue;
1596 switch (std.meta.stringToEnum(Directive, token) orelse continue) {
1597 .options => while (line_it.next()) |sub_tok| {
1598 var colon_it = mem.splitScalar(u8, sub_tok, ':');
1599 const name = colon_it.first();
1600 const value_txt = colon_it.next() orelse continue;
1601 const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) {
1602 error.Overflow => 255,
1603 error.InvalidCharacter => continue,
1604 };
1605 switch (std.meta.stringToEnum(Option, name) orelse continue) {
1606 .ndots => rc.ndots = @min(value, 15),
1607 .attempts => rc.attempts = @min(value, 10),
1608 .timeout => rc.timeout = @min(value, 60),
1609 }
1610 },
1611 .nameserver => {
1612 const ip_txt = line_it.next() orelse continue;
1613 try linuxLookupNameFromNumericUnspec(gpa, &rc.ns, ip_txt, 53);
1614 },
1615 .domain, .search => {
1616 rc.search.items.len = 0;
1617 try rc.search.appendSlice(gpa, line_it.rest());
1618 },
15651619 }
1566 } else if (mem.eql(u8, token, "nameserver")) {
1567 const ip_txt = line_it.next() orelse continue;
1568 try linuxLookupNameFromNumericUnspec(&rc.ns, ip_txt, 53);
1569 } else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) {
1570 rc.search.items.len = 0;
1571 try rc.search.appendSlice(line_it.rest());
1620 } else |err| switch (err) {
1621 error.EndOfStream => if (reader.bufferedLen() != 0) return error.EndOfStream,
1622 else => |e| return e,
15721623 }
1573 }
15741624
1575 if (rc.ns.items.len == 0) {
1576 return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53);
1625 if (rc.ns.items.len == 0) {
1626 return linuxLookupNameFromNumericUnspec(gpa, &rc.ns, "127.0.0.1", 53);
1627 }
15771628 }
1578}
15791629
1580fn linuxLookupNameFromNumericUnspec(
1581 addrs: *std.ArrayList(LookupAddr),
1582 name: []const u8,
1583 port: u16,
1584) !void {
1585 const addr = try Address.resolveIp(name, port);
1586 (try addrs.addOne()).* = LookupAddr{ .addr = addr };
1587}
1630 fn resMSendRc(
1631 rc: ResolvConf,
1632 queries: []const []const u8,
1633 answers: [][]u8,
1634 answer_bufs: []const []u8,
1635 ) !void {
1636 const gpa = rc.gpa;
1637 const timeout = 1000 * rc.timeout;
1638 const attempts = rc.attempts;
15881639
1589fn resMSendRc(
1590 queries: []const []const u8,
1591 answers: [][]u8,
1592 answer_bufs: []const []u8,
1593 rc: ResolvConf,
1594) !void {
1595 const timeout = 1000 * rc.timeout;
1596 const attempts = rc.attempts;
1640 var sl: posix.socklen_t = @sizeOf(posix.sockaddr.in);
1641 var family: posix.sa_family_t = posix.AF.INET;
15971642
1598 var sl: posix.socklen_t = @sizeOf(posix.sockaddr.in);
1599 var family: posix.sa_family_t = posix.AF.INET;
1643 var ns_list: ArrayList(Address) = .empty;
1644 defer ns_list.deinit(gpa);
16001645
1601 var ns_list = std.ArrayList(Address).init(rc.ns.allocator);
1602 defer ns_list.deinit();
1646 try ns_list.resize(gpa, rc.ns.items.len);
16031647
1604 try ns_list.resize(rc.ns.items.len);
1605 const ns = ns_list.items;
1606
1607 for (rc.ns.items, 0..) |iplit, i| {
1608 ns[i] = iplit.addr;
1609 assert(ns[i].getPort() == 53);
1610 if (iplit.addr.any.family != posix.AF.INET) {
1611 family = posix.AF.INET6;
1648 for (ns_list.items, rc.ns.items) |*ns, iplit| {
1649 ns.* = iplit.addr;
1650 assert(ns.getPort() == 53);
1651 if (iplit.addr.any.family != posix.AF.INET) {
1652 family = posix.AF.INET6;
1653 }
16121654 }
1613 }
16141655
1615 const flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC | posix.SOCK.NONBLOCK;
1616 const fd = posix.socket(family, flags, 0) catch |err| switch (err) {
1617 error.AddressFamilyNotSupported => blk: {
1618 // Handle case where system lacks IPv6 support
1619 if (family == posix.AF.INET6) {
1620 family = posix.AF.INET;
1621 break :blk try posix.socket(posix.AF.INET, flags, 0);
1656 const flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC | posix.SOCK.NONBLOCK;
1657 const fd = posix.socket(family, flags, 0) catch |err| switch (err) {
1658 error.AddressFamilyNotSupported => blk: {
1659 // Handle case where system lacks IPv6 support
1660 if (family == posix.AF.INET6) {
1661 family = posix.AF.INET;
1662 break :blk try posix.socket(posix.AF.INET, flags, 0);
1663 }
1664 return err;
1665 },
1666 else => |e| return e,
1667 };
1668 defer Stream.close(.{ .handle = fd });
1669
1670 // Past this point, there are no errors. Each individual query will
1671 // yield either no reply (indicated by zero length) or an answer
1672 // packet which is up to the caller to interpret.
1673
1674 // Convert any IPv4 addresses in a mixed environment to v4-mapped
1675 if (family == posix.AF.INET6) {
1676 try posix.setsockopt(
1677 fd,
1678 posix.SOL.IPV6,
1679 std.os.linux.IPV6.V6ONLY,
1680 &mem.toBytes(@as(c_int, 0)),
1681 );
1682 for (ns_list.items) |*ns| {
1683 if (ns.any.family != posix.AF.INET) continue;
1684 mem.writeInt(u32, ns.in6.sa.addr[12..], ns.in.sa.addr, native_endian);
1685 ns.in6.sa.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
1686 ns.any.family = posix.AF.INET6;
1687 ns.in6.sa.flowinfo = 0;
1688 ns.in6.sa.scope_id = 0;
16221689 }
1623 return err;
1624 },
1625 else => |e| return e,
1626 };
1627 defer Stream.close(.{ .handle = fd });
1628
1629 // Past this point, there are no errors. Each individual query will
1630 // yield either no reply (indicated by zero length) or an answer
1631 // packet which is up to the caller to interpret.
1632
1633 // Convert any IPv4 addresses in a mixed environment to v4-mapped
1634 if (family == posix.AF.INET6) {
1635 try posix.setsockopt(
1636 fd,
1637 posix.SOL.IPV6,
1638 std.os.linux.IPV6.V6ONLY,
1639 &mem.toBytes(@as(c_int, 0)),
1640 );
1641 for (0..ns.len) |i| {
1642 if (ns[i].any.family != posix.AF.INET) continue;
1643 mem.writeInt(u32, ns[i].in6.sa.addr[12..], ns[i].in.sa.addr, native_endian);
1644 ns[i].in6.sa.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
1645 ns[i].any.family = posix.AF.INET6;
1646 ns[i].in6.sa.flowinfo = 0;
1647 ns[i].in6.sa.scope_id = 0;
1690 sl = @sizeOf(posix.sockaddr.in6);
16481691 }
1649 sl = @sizeOf(posix.sockaddr.in6);
1650 }
1651
1652 // Get local address and open/bind a socket
1653 var sa: Address = undefined;
1654 @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0);
1655 sa.any.family = family;
1656 try posix.bind(fd, &sa.any, sl);
1657
1658 var pfd = [1]posix.pollfd{posix.pollfd{
1659 .fd = fd,
1660 .events = posix.POLL.IN,
1661 .revents = undefined,
1662 }};
1663 const retry_interval = timeout / attempts;
1664 var next: u32 = 0;
1665 var t2: u64 = @bitCast(std.time.milliTimestamp());
1666 const t0 = t2;
1667 var t1 = t2 - retry_interval;
1668
1669 var servfail_retry: usize = undefined;
1670
1671 outer: while (t2 - t0 < timeout) : (t2 = @as(u64, @bitCast(std.time.milliTimestamp()))) {
1672 if (t2 - t1 >= retry_interval) {
1673 // Query all configured nameservers in parallel
1674 var i: usize = 0;
1675 while (i < queries.len) : (i += 1) {
1676 if (answers[i].len == 0) {
1677 var j: usize = 0;
1678 while (j < ns.len) : (j += 1) {
1679 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined;
1692
1693 // Get local address and open/bind a socket
1694 var sa: Address = undefined;
1695 @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0);
1696 sa.any.family = family;
1697 try posix.bind(fd, &sa.any, sl);
1698
1699 var pfd = [1]posix.pollfd{posix.pollfd{
1700 .fd = fd,
1701 .events = posix.POLL.IN,
1702 .revents = undefined,
1703 }};
1704 const retry_interval = timeout / attempts;
1705 var next: u32 = 0;
1706 var t2: u64 = @bitCast(std.time.milliTimestamp());
1707 const t0 = t2;
1708 var t1 = t2 - retry_interval;
1709
1710 var servfail_retry: usize = undefined;
1711
1712 outer: while (t2 - t0 < timeout) : (t2 = @as(u64, @bitCast(std.time.milliTimestamp()))) {
1713 if (t2 - t1 >= retry_interval) {
1714 // Query all configured nameservers in parallel
1715 var i: usize = 0;
1716 while (i < queries.len) : (i += 1) {
1717 if (answers[i].len == 0) {
1718 for (ns_list.items) |*ns| {
1719 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns.any, sl) catch undefined;
1720 }
16801721 }
16811722 }
1723 t1 = t2;
1724 servfail_retry = 2 * queries.len;
16821725 }
1683 t1 = t2;
1684 servfail_retry = 2 * queries.len;
1685 }
16861726
1687 // Wait for a response, or until time to retry
1688 const clamped_timeout = @min(@as(u31, std.math.maxInt(u31)), t1 + retry_interval - t2);
1689 const nevents = posix.poll(&pfd, clamped_timeout) catch 0;
1690 if (nevents == 0) continue;
1727 // Wait for a response, or until time to retry
1728 const clamped_timeout = @min(@as(u31, std.math.maxInt(u31)), t1 + retry_interval - t2);
1729 const nevents = posix.poll(&pfd, clamped_timeout) catch 0;
1730 if (nevents == 0) continue;
1731
1732 while (true) {
1733 var sl_copy = sl;
1734 const rlen = posix.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;
1735
1736 // Ignore non-identifiable packets
1737 if (rlen < 4) continue;
1738
1739 // Ignore replies from addresses we didn't send to
1740 const ns = for (ns_list.items) |*ns| {
1741 if (ns.eql(sa)) break ns;
1742 } else continue;
1743
1744 // Find which query this answer goes with, if any
1745 var i: usize = next;
1746 while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or
1747 answer_bufs[next][1] != queries[i][1])) : (i += 1)
1748 {}
1749
1750 if (i == queries.len) continue;
1751 if (answers[i].len != 0) continue;
1752
1753 // Only accept positive or negative responses;
1754 // retry immediately on server failure, and ignore
1755 // all other codes such as refusal.
1756 switch (answer_bufs[next][3] & 15) {
1757 0, 3 => {},
1758 2 => if (servfail_retry != 0) {
1759 servfail_retry -= 1;
1760 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns.any, sl) catch undefined;
1761 },
1762 else => continue,
1763 }
16911764
1692 while (true) {
1693 var sl_copy = sl;
1694 const rlen = posix.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;
1695
1696 // Ignore non-identifiable packets
1697 if (rlen < 4) continue;
1698
1699 // Ignore replies from addresses we didn't send to
1700 var j: usize = 0;
1701 while (j < ns.len and !ns[j].eql(sa)) : (j += 1) {}
1702 if (j == ns.len) continue;
1703
1704 // Find which query this answer goes with, if any
1705 var i: usize = next;
1706 while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or
1707 answer_bufs[next][1] != queries[i][1])) : (i += 1)
1708 {}
1709
1710 if (i == queries.len) continue;
1711 if (answers[i].len != 0) continue;
1712
1713 // Only accept positive or negative responses;
1714 // retry immediately on server failure, and ignore
1715 // all other codes such as refusal.
1716 switch (answer_bufs[next][3] & 15) {
1717 0, 3 => {},
1718 2 => if (servfail_retry != 0) {
1719 servfail_retry -= 1;
1720 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined;
1721 },
1722 else => continue,
1723 }
1765 // Store answer in the right slot, or update next
1766 // available temp slot if it's already in place.
1767 answers[i].len = rlen;
1768 if (i == next) {
1769 while (next < queries.len and answers[next].len != 0) : (next += 1) {}
1770 } else {
1771 @memcpy(answer_bufs[i][0..rlen], answer_bufs[next][0..rlen]);
1772 }
17241773
1725 // Store answer in the right slot, or update next
1726 // available temp slot if it's already in place.
1727 answers[i].len = rlen;
1728 if (i == next) {
1729 while (next < queries.len and answers[next].len != 0) : (next += 1) {}
1730 } else {
1731 @memcpy(answer_bufs[i][0..rlen], answer_bufs[next][0..rlen]);
1774 if (next == queries.len) break :outer;
17321775 }
1733
1734 if (next == queries.len) break :outer;
17351776 }
17361777 }
1778
1779 fn deinit(rc: *ResolvConf) void {
1780 const gpa = rc.gpa;
1781 rc.ns.deinit(gpa);
1782 rc.search.deinit(gpa);
1783 rc.* = undefined;
1784 }
1785};
1786
1787fn linuxLookupNameFromNumericUnspec(
1788 gpa: Allocator,
1789 addrs: *ArrayList(LookupAddr),
1790 name: []const u8,
1791 port: u16,
1792) !void {
1793 const addr = try Address.resolveIp(name, port);
1794 try addrs.append(gpa, .{ .addr = addr });
17371795}
17381796
17391797fn dnsParse(
......@@ -1770,20 +1828,19 @@ fn dnsParse(
17701828}
17711829
17721830fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void {
1831 const gpa = ctx.gpa;
17731832 switch (rr) {
17741833 posix.RR.A => {
17751834 if (data.len != 4) return error.InvalidDnsARecord;
1776 const new_addr = try ctx.addrs.addOne();
1777 new_addr.* = LookupAddr{
1835 try ctx.addrs.append(gpa, .{
17781836 .addr = Address.initIp4(data[0..4].*, ctx.port),
1779 };
1837 });
17801838 },
17811839 posix.RR.AAAA => {
17821840 if (data.len != 16) return error.InvalidDnsAAAARecord;
1783 const new_addr = try ctx.addrs.addOne();
1784 new_addr.* = LookupAddr{
1841 try ctx.addrs.append(gpa, .{
17851842 .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0),
1786 };
1843 });
17871844 },
17881845 posix.RR.CNAME => {
17891846 var tmp: [256]u8 = undefined;
......@@ -1792,7 +1849,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
17921849 const canon_name = mem.sliceTo(&tmp, 0);
17931850 if (isValidHostName(canon_name)) {
17941851 ctx.canon.items.len = 0;
1795 try ctx.canon.appendSlice(canon_name);
1852 try ctx.canon.appendSlice(gpa, canon_name);
17961853 }
17971854 },
17981855 else => return,
......@@ -1802,7 +1859,12 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
18021859pub const Stream = struct {
18031860 /// Underlying platform-defined type which may or may not be
18041861 /// interchangeable with a file system file descriptor.
1805 handle: posix.socket_t,
1862 handle: Handle,
1863
1864 pub const Handle = switch (native_os) {
1865 .windows => windows.ws2_32.SOCKET,
1866 else => posix.fd_t,
1867 };
18061868
18071869 pub fn close(s: Stream) void {
18081870 switch (native_os) {
......@@ -1811,20 +1873,342 @@ pub const Stream = struct {
18111873 }
18121874 }
18131875
1814 pub const ReadError = posix.ReadError;
1815 pub const WriteError = posix.WriteError;
1876 pub const ReadError = posix.ReadError || error{
1877 SocketNotBound,
1878 MessageTooBig,
1879 NetworkSubsystemFailed,
1880 ConnectionResetByPeer,
1881 SocketNotConnected,
1882 };
1883
1884 pub const WriteError = posix.SendMsgError || error{
1885 ConnectionResetByPeer,
1886 SocketNotBound,
1887 MessageTooBig,
1888 NetworkSubsystemFailed,
1889 SystemResources,
1890 SocketNotConnected,
1891 Unexpected,
1892 };
1893
1894 pub const Reader = switch (native_os) {
1895 .windows => struct {
1896 /// Use `interface` for portable code.
1897 interface_state: io.Reader,
1898 /// Use `getStream` for portable code.
1899 net_stream: Stream,
1900 /// Use `getError` for portable code.
1901 error_state: ?Error,
1902
1903 pub const Error = ReadError;
1904
1905 pub fn getStream(r: *const Reader) Stream {
1906 return r.stream;
1907 }
1908
1909 pub fn getError(r: *const Reader) ?Error {
1910 return r.error_state;
1911 }
1912
1913 pub fn interface(r: *Reader) *io.Reader {
1914 return &r.interface_state;
1915 }
1916
1917 pub fn init(net_stream: Stream, buffer: []u8) Reader {
1918 return .{
1919 .interface_state = .{
1920 .vtable = &.{ .stream = stream },
1921 .buffer = buffer,
1922 .seek = 0,
1923 .end = 0,
1924 },
1925 .net_stream = net_stream,
1926 .error_state = null,
1927 };
1928 }
1929
1930 fn stream(io_r: *io.Reader, io_w: *io.Writer, limit: io.Limit) io.Reader.StreamError!usize {
1931 const r: *Reader = @alignCast(@fieldParentPtr("interface_state", io_r));
1932 var iovecs: [max_buffers_len]windows.ws2_32.WSABUF = undefined;
1933 const bufs = try io_w.writableVectorWsa(&iovecs, limit);
1934 assert(bufs[0].len != 0);
1935 const n = streamBufs(r, bufs) catch |err| {
1936 r.error_state = err;
1937 return error.ReadFailed;
1938 };
1939 if (n == 0) return error.EndOfStream;
1940 return n;
1941 }
1942
1943 fn streamBufs(r: *Reader, bufs: []windows.ws2_32.WSABUF) Error!u32 {
1944 var n: u32 = undefined;
1945 var flags: u32 = 0;
1946 const rc = windows.ws2_32.WSARecvFrom(r.net_stream.handle, bufs.ptr, @intCast(bufs.len), &n, &flags, null, null, null, null);
1947 if (rc != 0) switch (windows.ws2_32.WSAGetLastError()) {
1948 .WSAECONNRESET => return error.ConnectionResetByPeer,
1949 .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
1950 .WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.2
1951 .WSAEINVAL => return error.SocketNotBound,
1952 .WSAEMSGSIZE => return error.MessageTooBig,
1953 .WSAENETDOWN => return error.NetworkSubsystemFailed,
1954 .WSAENETRESET => return error.ConnectionResetByPeer,
1955 .WSAENOTCONN => return error.SocketNotConnected,
1956 .WSAEWOULDBLOCK => return error.WouldBlock,
1957 .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
1958 .WSA_IO_PENDING => unreachable, // not using overlapped I/O
1959 .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
1960 else => |err| return windows.unexpectedWSAError(err),
1961 };
1962 return n;
1963 }
1964 },
1965 else => struct {
1966 /// Use `getStream`, `interface`, and `getError` for portable code.
1967 file_reader: File.Reader,
1968
1969 pub const Error = ReadError;
1970
1971 pub fn interface(r: *Reader) *io.Reader {
1972 return &r.file_reader.interface;
1973 }
1974
1975 pub fn init(net_stream: Stream, buffer: []u8) Reader {
1976 return .{
1977 .file_reader = .{
1978 .interface = File.Reader.initInterface(buffer),
1979 .file = .{ .handle = net_stream.handle },
1980 .mode = .streaming,
1981 .seek_err = error.Unseekable,
1982 },
1983 };
1984 }
1985
1986 pub fn getStream(r: *const Reader) Stream {
1987 return .{ .handle = r.file_reader.file.handle };
1988 }
1989
1990 pub fn getError(r: *const Reader) ?Error {
1991 return r.file_reader.err;
1992 }
1993 },
1994 };
1995
1996 pub const Writer = switch (native_os) {
1997 .windows => struct {
1998 /// This field is present on all systems.
1999 interface: io.Writer,
2000 /// Use `getStream` for cross-platform support.
2001 stream: Stream,
2002 /// This field is present on all systems.
2003 err: ?Error = null,
2004
2005 pub const Error = WriteError;
2006
2007 pub fn init(stream: Stream, buffer: []u8) Writer {
2008 return .{
2009 .stream = stream,
2010 .interface = .{
2011 .vtable = &.{ .drain = drain },
2012 .buffer = buffer,
2013 },
2014 };
2015 }
2016
2017 pub fn getStream(w: *const Writer) Stream {
2018 return w.stream;
2019 }
18162020
1817 pub const Reader = io.GenericReader(Stream, ReadError, read);
1818 pub const Writer = io.GenericWriter(Stream, WriteError, write);
2021 fn addWsaBuf(v: []windows.ws2_32.WSABUF, i: *u32, bytes: []const u8) void {
2022 const cap = std.math.maxInt(u32);
2023 var remaining = bytes;
2024 while (remaining.len > cap) {
2025 if (v.len - i.* == 0) return;
2026 v[i.*] = .{ .buf = @constCast(remaining.ptr), .len = cap };
2027 i.* += 1;
2028 remaining = remaining[cap..];
2029 } else {
2030 @branchHint(.likely);
2031 if (v.len - i.* == 0) return;
2032 v[i.*] = .{ .buf = @constCast(remaining.ptr), .len = @intCast(remaining.len) };
2033 i.* += 1;
2034 }
2035 }
18192036
1820 pub fn reader(self: Stream) Reader {
1821 return .{ .context = self };
2037 fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize {
2038 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
2039 const buffered = io_w.buffered();
2040 comptime assert(native_os == .windows);
2041 var iovecs: [max_buffers_len]windows.ws2_32.WSABUF = undefined;
2042 var len: u32 = 0;
2043 addWsaBuf(&iovecs, &len, buffered);
2044 for (data[0 .. data.len - 1]) |bytes| addWsaBuf(&iovecs, &len, bytes);
2045 const pattern = data[data.len - 1];
2046 if (iovecs.len - len != 0) switch (splat) {
2047 0 => {},
2048 1 => addWsaBuf(&iovecs, &len, pattern),
2049 else => switch (pattern.len) {
2050 0 => {},
2051 1 => {
2052 const splat_buffer_candidate = io_w.buffer[io_w.end..];
2053 var backup_buffer: [64]u8 = undefined;
2054 const splat_buffer = if (splat_buffer_candidate.len >= backup_buffer.len)
2055 splat_buffer_candidate
2056 else
2057 &backup_buffer;
2058 const memset_len = @min(splat_buffer.len, splat);
2059 const buf = splat_buffer[0..memset_len];
2060 @memset(buf, pattern[0]);
2061 addWsaBuf(&iovecs, &len, buf);
2062 var remaining_splat = splat - buf.len;
2063 while (remaining_splat > splat_buffer.len and len < iovecs.len) {
2064 addWsaBuf(&iovecs, &len, splat_buffer);
2065 remaining_splat -= splat_buffer.len;
2066 }
2067 addWsaBuf(&iovecs, &len, splat_buffer[0..remaining_splat]);
2068 },
2069 else => for (0..@min(splat, iovecs.len - len)) |_| {
2070 addWsaBuf(&iovecs, &len, pattern);
2071 },
2072 },
2073 };
2074 const n = sendBufs(w.stream.handle, iovecs[0..len]) catch |err| {
2075 w.err = err;
2076 return error.WriteFailed;
2077 };
2078 return io_w.consume(n);
2079 }
2080
2081 fn sendBufs(handle: Stream.Handle, bufs: []windows.ws2_32.WSABUF) Error!u32 {
2082 var n: u32 = undefined;
2083 const rc = windows.ws2_32.WSASend(handle, bufs.ptr, @intCast(bufs.len), &n, 0, null, null);
2084 if (rc == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) {
2085 .WSAECONNABORTED => return error.ConnectionResetByPeer,
2086 .WSAECONNRESET => return error.ConnectionResetByPeer,
2087 .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
2088 .WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.2
2089 .WSAEINVAL => return error.SocketNotBound,
2090 .WSAEMSGSIZE => return error.MessageTooBig,
2091 .WSAENETDOWN => return error.NetworkSubsystemFailed,
2092 .WSAENETRESET => return error.ConnectionResetByPeer,
2093 .WSAENOBUFS => return error.SystemResources,
2094 .WSAENOTCONN => return error.SocketNotConnected,
2095 .WSAENOTSOCK => unreachable, // not a socket
2096 .WSAEOPNOTSUPP => unreachable, // only for message-oriented sockets
2097 .WSAESHUTDOWN => unreachable, // cannot send on a socket after write shutdown
2098 .WSAEWOULDBLOCK => return error.WouldBlock,
2099 .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
2100 .WSA_IO_PENDING => unreachable, // not using overlapped I/O
2101 .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
2102 else => |err| return windows.unexpectedWSAError(err),
2103 };
2104 return n;
2105 }
2106 },
2107 else => struct {
2108 /// This field is present on all systems.
2109 interface: io.Writer,
2110
2111 err: ?Error = null,
2112 file_writer: File.Writer,
2113
2114 pub const Error = WriteError;
2115
2116 pub fn init(stream: Stream, buffer: []u8) Writer {
2117 return .{
2118 .interface = .{
2119 .vtable = &.{
2120 .drain = drain,
2121 .sendFile = sendFile,
2122 },
2123 .buffer = buffer,
2124 },
2125 .file_writer = .initMode(.{ .handle = stream.handle }, &.{}, .streaming),
2126 };
2127 }
2128
2129 pub fn getStream(w: *const Writer) Stream {
2130 return .{ .handle = w.file_writer.file.handle };
2131 }
2132
2133 fn addBuf(v: []posix.iovec_const, i: *@FieldType(posix.msghdr_const, "iovlen"), bytes: []const u8) void {
2134 // OS checks ptr addr before length so zero length vectors must be omitted.
2135 if (bytes.len == 0) return;
2136 if (v.len - i.* == 0) return;
2137 v[i.*] = .{ .base = bytes.ptr, .len = bytes.len };
2138 i.* += 1;
2139 }
2140
2141 fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize {
2142 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
2143 const buffered = io_w.buffered();
2144 var iovecs: [max_buffers_len]posix.iovec_const = undefined;
2145 var msg: posix.msghdr_const = .{
2146 .name = null,
2147 .namelen = 0,
2148 .iov = &iovecs,
2149 .iovlen = 0,
2150 .control = null,
2151 .controllen = 0,
2152 .flags = 0,
2153 };
2154 addBuf(&iovecs, &msg.iovlen, buffered);
2155 for (data[0 .. data.len - 1]) |bytes| addBuf(&iovecs, &msg.iovlen, bytes);
2156 const pattern = data[data.len - 1];
2157 if (iovecs.len - msg.iovlen != 0) switch (splat) {
2158 0 => {},
2159 1 => addBuf(&iovecs, &msg.iovlen, pattern),
2160 else => switch (pattern.len) {
2161 0 => {},
2162 1 => {
2163 const splat_buffer_candidate = io_w.buffer[io_w.end..];
2164 var backup_buffer: [64]u8 = undefined;
2165 const splat_buffer = if (splat_buffer_candidate.len >= backup_buffer.len)
2166 splat_buffer_candidate
2167 else
2168 &backup_buffer;
2169 const memset_len = @min(splat_buffer.len, splat);
2170 const buf = splat_buffer[0..memset_len];
2171 @memset(buf, pattern[0]);
2172 addBuf(&iovecs, &msg.iovlen, buf);
2173 var remaining_splat = splat - buf.len;
2174 while (remaining_splat > splat_buffer.len and iovecs.len - msg.iovlen != 0) {
2175 assert(buf.len == splat_buffer.len);
2176 addBuf(&iovecs, &msg.iovlen, splat_buffer);
2177 remaining_splat -= splat_buffer.len;
2178 }
2179 addBuf(&iovecs, &msg.iovlen, splat_buffer[0..remaining_splat]);
2180 },
2181 else => for (0..@min(splat, iovecs.len - msg.iovlen)) |_| {
2182 addBuf(&iovecs, &msg.iovlen, pattern);
2183 },
2184 },
2185 };
2186 const flags = posix.MSG.NOSIGNAL;
2187 return io_w.consume(posix.sendmsg(w.file_writer.file.handle, &msg, flags) catch |err| {
2188 w.err = err;
2189 return error.WriteFailed;
2190 });
2191 }
2192
2193 fn sendFile(io_w: *io.Writer, file_reader: *File.Reader, limit: io.Limit) io.Writer.FileError!usize {
2194 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
2195 const n = try w.file_writer.interface.sendFileHeader(io_w.buffered(), file_reader, limit);
2196 return io_w.consume(n);
2197 }
2198 },
2199 };
2200
2201 pub fn reader(stream: Stream, buffer: []u8) Reader {
2202 return .init(stream, buffer);
18222203 }
18232204
1824 pub fn writer(self: Stream) Writer {
1825 return .{ .context = self };
2205 pub fn writer(stream: Stream, buffer: []u8) Writer {
2206 return .init(stream, buffer);
18262207 }
18272208
2209 const max_buffers_len = 8;
2210
2211 /// Deprecated in favor of `Reader`.
18282212 pub fn read(self: Stream, buffer: []u8) ReadError!usize {
18292213 if (native_os == .windows) {
18302214 return windows.ReadFile(self.handle, buffer, null);
......@@ -1833,10 +2217,10 @@ pub const Stream = struct {
18332217 return posix.read(self.handle, buffer);
18342218 }
18352219
2220 /// Deprecated in favor of `Reader`.
18362221 pub fn readv(s: Stream, iovecs: []const posix.iovec) ReadError!usize {
18372222 if (native_os == .windows) {
1838 // TODO improve this to use ReadFileScatter
1839 if (iovecs.len == 0) return @as(usize, 0);
2223 if (iovecs.len == 0) return 0;
18402224 const first = iovecs[0];
18412225 return windows.ReadFile(s.handle, first.base[0..first.len], null);
18422226 }
......@@ -1844,18 +2228,7 @@ pub const Stream = struct {
18442228 return posix.readv(s.handle, iovecs);
18452229 }
18462230
1847 /// Returns the number of bytes read. If the number read is smaller than
1848 /// `buffer.len`, it means the stream reached the end. Reaching the end of
1849 /// a stream is not an error condition.
1850 pub fn readAll(s: Stream, buffer: []u8) ReadError!usize {
1851 return readAtLeast(s, buffer, buffer.len);
1852 }
1853
1854 /// Returns the number of bytes read, calling the underlying read function
1855 /// the minimal number of times until the buffer has at least `len` bytes
1856 /// filled. If the number read is less than `len` it means the stream
1857 /// reached the end. Reaching the end of the stream is not an error
1858 /// condition.
2231 /// Deprecated in favor of `Reader`.
18592232 pub fn readAtLeast(s: Stream, buffer: []u8, len: usize) ReadError!usize {
18602233 assert(len <= buffer.len);
18612234 var index: usize = 0;
......@@ -1867,17 +2240,13 @@ pub const Stream = struct {
18672240 return index;
18682241 }
18692242
1870 /// TODO in evented I/O mode, this implementation incorrectly uses the event loop's
1871 /// file system thread instead of non-blocking. It needs to be reworked to properly
1872 /// use non-blocking I/O.
2243 /// Deprecated in favor of `Writer`.
18732244 pub fn write(self: Stream, buffer: []const u8) WriteError!usize {
1874 if (native_os == .windows) {
1875 return windows.WriteFile(self.handle, buffer, null);
1876 }
1877
1878 return posix.write(self.handle, buffer);
2245 var stream_writer = self.writer(&.{});
2246 return stream_writer.interface.writeVec(&.{buffer}) catch return stream_writer.err.?;
18792247 }
18802248
2249 /// Deprecated in favor of `Writer`.
18812250 pub fn writeAll(self: Stream, bytes: []const u8) WriteError!void {
18822251 var index: usize = 0;
18832252 while (index < bytes.len) {
......@@ -1885,16 +2254,12 @@ pub const Stream = struct {
18852254 }
18862255 }
18872256
1888 /// See https://github.com/ziglang/zig/issues/7699
1889 /// See equivalent function: `std.fs.File.writev`.
2257 /// Deprecated in favor of `Writer`.
18902258 pub fn writev(self: Stream, iovecs: []const posix.iovec_const) WriteError!usize {
1891 return posix.writev(self.handle, iovecs);
2259 return @errorCast(posix.writev(self.handle, iovecs));
18922260 }
18932261
1894 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
1895 /// order to handle partial writes from the underlying OS layer.
1896 /// See https://github.com/ziglang/zig/issues/7699
1897 /// See equivalent function: `std.fs.File.writevAll`.
2262 /// Deprecated in favor of `Writer`.
18982263 pub fn writevAll(self: Stream, iovecs: []posix.iovec_const) WriteError!void {
18992264 if (iovecs.len == 0) return;
19002265
......@@ -1914,10 +2279,10 @@ pub const Stream = struct {
19142279
19152280pub const Server = struct {
19162281 listen_address: Address,
1917 stream: std.net.Stream,
2282 stream: Stream,
19182283
19192284 pub const Connection = struct {
1920 stream: std.net.Stream,
2285 stream: Stream,
19212286 address: Address,
19222287 };
19232288
lib/std/net/test.zig+8-4
......@@ -208,7 +208,8 @@ test "listen on a port, send bytes, receive bytes" {
208208 const socket = try net.tcpConnectToAddress(server_address);
209209 defer socket.close();
210210
211 _ = try socket.writer().writeAll("Hello world!");
211 var stream_writer = socket.writer(&.{});
212 try stream_writer.interface.writeAll("Hello world!");
212213 }
213214 };
214215
......@@ -218,7 +219,8 @@ test "listen on a port, send bytes, receive bytes" {
218219 var client = try server.accept();
219220 defer client.stream.close();
220221 var buf: [16]u8 = undefined;
221 const n = try client.stream.reader().read(&buf);
222 var stream_reader = client.stream.reader(&.{});
223 const n = try stream_reader.interface().readSliceShort(&buf);
222224
223225 try testing.expectEqual(@as(usize, 12), n);
224226 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
......@@ -299,7 +301,8 @@ test "listen on a unix socket, send bytes, receive bytes" {
299301 const socket = try net.connectUnixSocket(path);
300302 defer socket.close();
301303
302 _ = try socket.writer().writeAll("Hello world!");
304 var stream_writer = socket.writer(&.{});
305 try stream_writer.interface.writeAll("Hello world!");
303306 }
304307 };
305308
......@@ -309,7 +312,8 @@ test "listen on a unix socket, send bytes, receive bytes" {
309312 var client = try server.accept();
310313 defer client.stream.close();
311314 var buf: [16]u8 = undefined;
312 const n = try client.stream.reader().read(&buf);
315 var stream_reader = client.stream.reader(&.{});
316 const n = try stream_reader.interface().readSliceShort(&buf);
313317
314318 try testing.expectEqual(@as(usize, 12), n);
315319 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
src/IncrementalDebugServer.zig+4-3
......@@ -55,14 +55,15 @@ fn runThread(ids: *IncrementalDebugServer) void {
5555 const conn = server.accept() catch @panic("IncrementalDebugServer: failed to accept");
5656 defer conn.stream.close();
5757
58 var stream_reader = conn.stream.reader(&cmd_buf);
59
5860 while (ids.running.load(.monotonic)) {
5961 conn.stream.writeAll("zig> ") catch @panic("IncrementalDebugServer: failed to write");
60 var fbs = std.io.fixedBufferStream(&cmd_buf);
61 conn.stream.reader().streamUntilDelimiter(fbs.writer(), '\n', cmd_buf.len) catch |err| switch (err) {
62 const untrimmed = stream_reader.interface().takeSentinel('\n') catch |err| switch (err) {
6263 error.EndOfStream => break,
6364 else => @panic("IncrementalDebugServer: failed to read command"),
6465 };
65 const cmd_and_arg = std.mem.trim(u8, fbs.getWritten(), " \t\r\n");
66 const cmd_and_arg = std.mem.trim(u8, untrimmed, " \t\r\n");
6667 const cmd: []const u8, const arg: []const u8 = if (std.mem.indexOfScalar(u8, cmd_and_arg, ' ')) |i|
6768 .{ cmd_and_arg[0..i], cmd_and_arg[i + 1 ..] }
6869 else