authorgravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-04-12 22:48:03-05:00
committergravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-04-17 19:14:48-05:00
log038ed32cffbb40d87d8634470e29df31b7699359
tree7248b4e0c329644c16f0387e0af876a3f4572c04
parent40e1fca34b2c0d2cde130fd17331a2935c473644
signaturelock-open Commit is signed but in an unrecognized format.

add explicit error union for Bundle.rescan and associated functions


4 files changed, 54 insertions(+), 43 deletions(-)

lib/std/crypto/Certificate.zig+21-11
...@@ -371,7 +371,9 @@ test "Parsed.checkHostName" {...@@ -371,7 +371,9 @@ test "Parsed.checkHostName" {
371 try expectEqual(false, Parsed.checkHostName("lang.org", "zig*.org"));371 try expectEqual(false, Parsed.checkHostName("lang.org", "zig*.org"));
372}372}
373373
374pub fn parse(cert: Certificate) !Parsed {374pub const ParseError = der.Element.ParseElementError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
375
376pub fn parse(cert: Certificate) ParseError!Parsed {
375 const cert_bytes = cert.buffer;377 const cert_bytes = cert.buffer;
376 const certificate = try der.Element.parse(cert_bytes, cert.index);378 const certificate = try der.Element.parse(cert_bytes, cert.index);
377 const tbs_certificate = try der.Element.parse(cert_bytes, certificate.slice.start);379 const tbs_certificate = try der.Element.parse(cert_bytes, certificate.slice.start);
...@@ -514,14 +516,18 @@ pub fn contents(cert: Certificate, elem: der.Element) []const u8 {...@@ -514,14 +516,18 @@ pub fn contents(cert: Certificate, elem: der.Element) []const u8 {
514 return cert.buffer[elem.slice.start..elem.slice.end];516 return cert.buffer[elem.slice.start..elem.slice.end];
515}517}
516518
519pub const ParseBitStringError = error{ CertificateFieldHasWrongDataType, CertificateHasInvalidBitString };
520
517pub fn parseBitString(cert: Certificate, elem: der.Element) !der.Element.Slice {521pub fn parseBitString(cert: Certificate, elem: der.Element) !der.Element.Slice {
518 if (elem.identifier.tag != .bitstring) return error.CertificateFieldHasWrongDataType;522 if (elem.identifier.tag != .bitstring) return error.CertificateFieldHasWrongDataType;
519 if (cert.buffer[elem.slice.start] != 0) return error.CertificateHasInvalidBitString;523 if (cert.buffer[elem.slice.start] != 0) return error.CertificateHasInvalidBitString;
520 return .{ .start = elem.slice.start + 1, .end = elem.slice.end };524 return .{ .start = elem.slice.start + 1, .end = elem.slice.end };
521}525}
522526
527pub const ParseTimeError = error{ CertificateTimeInvalid, CertificateFieldHasWrongDataType };
528
523/// Returns number of seconds since epoch.529/// Returns number of seconds since epoch.
524pub fn parseTime(cert: Certificate, elem: der.Element) !u64 {530pub fn parseTime(cert: Certificate, elem: der.Element) ParseTimeError!u64 {
525 const bytes = cert.contents(elem);531 const bytes = cert.contents(elem);
526 switch (elem.identifier.tag) {532 switch (elem.identifier.tag) {
527 .utc_time => {533 .utc_time => {
...@@ -647,34 +653,38 @@ test parseYear4 {...@@ -647,34 +653,38 @@ test parseYear4 {
647 try expectError(error.CertificateTimeInvalid, parseYear4("crap"));653 try expectError(error.CertificateTimeInvalid, parseYear4("crap"));
648}654}
649655
650pub fn parseAlgorithm(bytes: []const u8, element: der.Element) !Algorithm {656pub fn parseAlgorithm(bytes: []const u8, element: der.Element) ParseEnumError!Algorithm {
651 return parseEnum(Algorithm, bytes, element);657 return parseEnum(Algorithm, bytes, element);
652}658}
653659
654pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) !AlgorithmCategory {660pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) ParseEnumError!AlgorithmCategory {
655 return parseEnum(AlgorithmCategory, bytes, element);661 return parseEnum(AlgorithmCategory, bytes, element);
656}662}
657663
658pub fn parseAttribute(bytes: []const u8, element: der.Element) !Attribute {664pub fn parseAttribute(bytes: []const u8, element: der.Element) ParseEnumError!Attribute {
659 return parseEnum(Attribute, bytes, element);665 return parseEnum(Attribute, bytes, element);
660}666}
661667
662pub fn parseNamedCurve(bytes: []const u8, element: der.Element) !NamedCurve {668pub fn parseNamedCurve(bytes: []const u8, element: der.Element) ParseEnumError!NamedCurve {
663 return parseEnum(NamedCurve, bytes, element);669 return parseEnum(NamedCurve, bytes, element);
664}670}
665671
666pub fn parseExtensionId(bytes: []const u8, element: der.Element) !ExtensionId {672pub fn parseExtensionId(bytes: []const u8, element: der.Element) ParseEnumError!ExtensionId {
667 return parseEnum(ExtensionId, bytes, element);673 return parseEnum(ExtensionId, bytes, element);
668}674}
669675
670fn parseEnum(comptime E: type, bytes: []const u8, element: der.Element) !E {676pub const ParseEnumError = error{ CertificateFieldHasWrongDataType, CertificateHasUnrecognizedObjectId };
677
678fn parseEnum(comptime E: type, bytes: []const u8, element: der.Element) ParseEnumError!E {
671 if (element.identifier.tag != .object_identifier)679 if (element.identifier.tag != .object_identifier)
672 return error.CertificateFieldHasWrongDataType;680 return error.CertificateFieldHasWrongDataType;
673 const oid_bytes = bytes[element.slice.start..element.slice.end];681 const oid_bytes = bytes[element.slice.start..element.slice.end];
674 return E.map.get(oid_bytes) orelse return error.CertificateHasUnrecognizedObjectId;682 return E.map.get(oid_bytes) orelse return error.CertificateHasUnrecognizedObjectId;
675}683}
676684
677pub fn parseVersion(bytes: []const u8, version_elem: der.Element) !Version {685pub const ParseVersionError = error{ UnsupportedCertificateVersion, CertificateFieldHasInvalidLength };
686
687pub fn parseVersion(bytes: []const u8, version_elem: der.Element) ParseVersionError!Version {
678 if (@bitCast(u8, version_elem.identifier) != 0xa0)688 if (@bitCast(u8, version_elem.identifier) != 0xa0)
679 return .v1;689 return .v1;
680690
...@@ -861,9 +871,9 @@ pub const der = struct {...@@ -861,9 +871,9 @@ pub const der = struct {
861 pub const empty: Slice = .{ .start = 0, .end = 0 };871 pub const empty: Slice = .{ .start = 0, .end = 0 };
862 };872 };
863873
864 pub const ParseError = error{CertificateFieldHasInvalidLength};874 pub const ParseElementError = error{CertificateFieldHasInvalidLength};
865875
866 pub fn parse(bytes: []const u8, index: u32) ParseError!Element {876 pub fn parse(bytes: []const u8, index: u32) ParseElementError!Element {
867 var i = index;877 var i = index;
868 const identifier = @bitCast(Identifier, bytes[i]);878 const identifier = @bitCast(Identifier, bytes[i]);
869 i += 1;879 i += 1;
lib/std/crypto/Certificate/Bundle.zig+27-10
...@@ -50,11 +50,13 @@ pub fn deinit(cb: *Bundle, gpa: Allocator) void {...@@ -50,11 +50,13 @@ pub fn deinit(cb: *Bundle, gpa: Allocator) void {
50 cb.* = undefined;50 cb.* = undefined;
51}51}
5252
53pub const RescanError = RescanLinuxError || RescanMacError || RescanWindowsError;
54
53/// Clears the set of certificates and then scans the host operating system55/// Clears the set of certificates and then scans the host operating system
54/// file system standard locations for certificates.56/// file system standard locations for certificates.
55/// For operating systems that do not have standard CA installations to be57/// For operating systems that do not have standard CA installations to be
56/// found, this function clears the set of certificates.58/// found, this function clears the set of certificates.
57pub fn rescan(cb: *Bundle, gpa: Allocator) !void {59pub fn rescan(cb: *Bundle, gpa: Allocator) RescanError!void {
58 switch (builtin.os.tag) {60 switch (builtin.os.tag) {
59 .linux => return rescanLinux(cb, gpa),61 .linux => return rescanLinux(cb, gpa),
60 .macos => return rescanMac(cb, gpa),62 .macos => return rescanMac(cb, gpa),
...@@ -64,8 +66,11 @@ pub fn rescan(cb: *Bundle, gpa: Allocator) !void {...@@ -64,8 +66,11 @@ pub fn rescan(cb: *Bundle, gpa: Allocator) !void {
64}66}
6567
66pub const rescanMac = @import("Bundle/macos.zig").rescanMac;68pub const rescanMac = @import("Bundle/macos.zig").rescanMac;
69pub const RescanMacError = @import("Bundle/macos.zig").RescanMacError;
70
71pub const RescanLinuxError = AddCertsFromFilePathError || AddCertsFromDirPathError;
6772
68pub fn rescanLinux(cb: *Bundle, gpa: Allocator) !void {73pub fn rescanLinux(cb: *Bundle, gpa: Allocator) RescanLinuxError!void {
69 // Possible certificate files; stop after finding one.74 // Possible certificate files; stop after finding one.
70 const cert_file_paths = [_][]const u8{75 const cert_file_paths = [_][]const u8{
71 "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.76 "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
...@@ -107,7 +112,9 @@ pub fn rescanLinux(cb: *Bundle, gpa: Allocator) !void {...@@ -107,7 +112,9 @@ pub fn rescanLinux(cb: *Bundle, gpa: Allocator) !void {
107 cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);112 cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
108}113}
109114
110pub fn rescanWindows(cb: *Bundle, gpa: Allocator) !void {115pub const RescanWindowsError = Allocator.Error || ParseCertError || std.os.UnexpectedError || error{FileNotFound};
116
117pub fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void {
111 cb.bytes.clearRetainingCapacity();118 cb.bytes.clearRetainingCapacity();
112 cb.map.clearRetainingCapacity();119 cb.map.clearRetainingCapacity();
113120
...@@ -132,12 +139,14 @@ pub fn rescanWindows(cb: *Bundle, gpa: Allocator) !void {...@@ -132,12 +139,14 @@ pub fn rescanWindows(cb: *Bundle, gpa: Allocator) !void {
132 cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);139 cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
133}140}
134141
142pub const AddCertsFromDirPathError = fs.File.OpenError || AddCertsFromDirError;
143
135pub fn addCertsFromDirPath(144pub fn addCertsFromDirPath(
136 cb: *Bundle,145 cb: *Bundle,
137 gpa: Allocator,146 gpa: Allocator,
138 dir: fs.Dir,147 dir: fs.Dir,
139 sub_dir_path: []const u8,148 sub_dir_path: []const u8,
140) !void {149) AddCertsFromDirPathError!void {
141 var iterable_dir = try dir.openIterableDir(sub_dir_path, .{});150 var iterable_dir = try dir.openIterableDir(sub_dir_path, .{});
142 defer iterable_dir.close();151 defer iterable_dir.close();
143 return addCertsFromDir(cb, gpa, iterable_dir);152 return addCertsFromDir(cb, gpa, iterable_dir);
...@@ -147,14 +156,16 @@ pub fn addCertsFromDirPathAbsolute(...@@ -147,14 +156,16 @@ pub fn addCertsFromDirPathAbsolute(
147 cb: *Bundle,156 cb: *Bundle,
148 gpa: Allocator,157 gpa: Allocator,
149 abs_dir_path: []const u8,158 abs_dir_path: []const u8,
150) !void {159) AddCertsFromDirPathError!void {
151 assert(fs.path.isAbsolute(abs_dir_path));160 assert(fs.path.isAbsolute(abs_dir_path));
152 var iterable_dir = try fs.openIterableDirAbsolute(abs_dir_path, .{});161 var iterable_dir = try fs.openIterableDirAbsolute(abs_dir_path, .{});
153 defer iterable_dir.close();162 defer iterable_dir.close();
154 return addCertsFromDir(cb, gpa, iterable_dir);163 return addCertsFromDir(cb, gpa, iterable_dir);
155}164}
156165
157pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.IterableDir) !void {166pub const AddCertsFromDirError = AddCertsFromFilePathError;
167
168pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.IterableDir) AddCertsFromDirError!void {
158 var it = iterable_dir.iterate();169 var it = iterable_dir.iterate();
159 while (try it.next()) |entry| {170 while (try it.next()) |entry| {
160 switch (entry.kind) {171 switch (entry.kind) {
...@@ -166,11 +177,13 @@ pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.IterableDir...@@ -166,11 +177,13 @@ pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.IterableDir
166 }177 }
167}178}
168179
180pub const AddCertsFromFilePathError = fs.File.OpenError || AddCertsFromFileError;
181
169pub fn addCertsFromFilePathAbsolute(182pub fn addCertsFromFilePathAbsolute(
170 cb: *Bundle,183 cb: *Bundle,
171 gpa: Allocator,184 gpa: Allocator,
172 abs_file_path: []const u8,185 abs_file_path: []const u8,
173) !void {186) AddCertsFromFilePathError!void {
174 assert(fs.path.isAbsolute(abs_file_path));187 assert(fs.path.isAbsolute(abs_file_path));
175 var file = try fs.openFileAbsolute(abs_file_path, .{});188 var file = try fs.openFileAbsolute(abs_file_path, .{});
176 defer file.close();189 defer file.close();
...@@ -182,13 +195,15 @@ pub fn addCertsFromFilePath(...@@ -182,13 +195,15 @@ pub fn addCertsFromFilePath(
182 gpa: Allocator,195 gpa: Allocator,
183 dir: fs.Dir,196 dir: fs.Dir,
184 sub_file_path: []const u8,197 sub_file_path: []const u8,
185) !void {198) AddCertsFromFilePathError!void {
186 var file = try dir.openFile(sub_file_path, .{});199 var file = try dir.openFile(sub_file_path, .{});
187 defer file.close();200 defer file.close();
188 return addCertsFromFile(cb, gpa, file);201 return addCertsFromFile(cb, gpa, file);
189}202}
190203
191pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) !void {204pub const AddCertsFromFileError = Allocator.Error || fs.File.GetSeekPosError || fs.File.ReadError || ParseCertError || std.base64.Error || error{ CertificateAuthorityBundleTooBig, MissingEndCertificateMarker };
205
206pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFromFileError!void {
192 const size = try file.getEndPos();207 const size = try file.getEndPos();
193208
194 // We borrow `bytes` as a temporary buffer for the base64-encoded data.209 // We borrow `bytes` as a temporary buffer for the base64-encoded data.
...@@ -222,7 +237,9 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) !void {...@@ -222,7 +237,9 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) !void {
222 }237 }
223}238}
224239
225pub fn parseCert(cb: *Bundle, gpa: Allocator, decoded_start: u32, now_sec: i64) !void {240pub const ParseCertError = Allocator.Error || Certificate.ParseError;
241
242pub fn parseCert(cb: *Bundle, gpa: Allocator, decoded_start: u32, now_sec: i64) ParseCertError!void {
226 // Even though we could only partially parse the certificate to find243 // Even though we could only partially parse the certificate to find
227 // the subject name, we pre-parse all of them to make sure and only244 // the subject name, we pre-parse all of them to make sure and only
228 // include in the bundle ones that we know will parse. This way we can245 // include in the bundle ones that we know will parse. This way we can
lib/std/crypto/Certificate/Bundle/macos.zig+3-1
...@@ -5,7 +5,9 @@ const mem = std.mem;...@@ -5,7 +5,9 @@ const mem = std.mem;
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const Bundle = @import("../Bundle.zig");6const Bundle = @import("../Bundle.zig");
77
8pub fn rescanMac(cb: *Bundle, gpa: Allocator) !void {8pub const RescanMacError = Allocator.Error || fs.File.OpenError || fs.File.ReadError || fs.File.SeekError || Bundle.ParseCertError || error{EndOfStream};
9
10pub fn rescanMac(cb: *Bundle, gpa: Allocator) RescanMacError!void {
9 cb.bytes.clearRetainingCapacity();11 cb.bytes.clearRetainingCapacity();
10 cb.map.clearRetainingCapacity();12 cb.map.clearRetainingCapacity();
1113
lib/std/http/Client.zig+3-21
...@@ -29,34 +29,16 @@ connection_pool: ConnectionPool = .{},...@@ -29,34 +29,16 @@ connection_pool: ConnectionPool = .{},
29last_error: ?ExtraError = null,29last_error: ?ExtraError = null,
3030
31pub const ExtraError = union(enum) {31pub const ExtraError = union(enum) {
32 fn impliedErrorSet(comptime f: anytype) type {
33 const set = @typeInfo(@typeInfo(@TypeOf(f)).Fn.return_type.?).ErrorUnion.error_set;
34 if (@typeName(set)[0] != '@') @compileError(@typeName(f) ++ " doesn't have an implied error set any more.");
35 return set;
36 }
37
38 // There's apparently a dependency loop with using Client.DeflateDecompressor.
39 const FakeTransferError = proto.HeadersParser.ReadError || error{ReadFailed};
40 const FakeTransferReader = std.io.Reader(void, FakeTransferError, fakeRead);
41 fn fakeRead(ctx: void, buf: []u8) FakeTransferError!usize {
42 _ = .{ buf, ctx };
43 return 0;
44 }
45
46 const FakeDeflateDecompressor = std.compress.zlib.ZlibStream(FakeTransferReader);
47 const FakeGzipDecompressor = std.compress.gzip.Decompress(FakeTransferReader);
48 const FakeZstdDecompressor = std.compress.zstd.DecompressStream(FakeTransferReader, .{});
49
50 pub const TcpConnectError = std.net.TcpConnectToHostError;32 pub const TcpConnectError = std.net.TcpConnectToHostError;
51 pub const TlsError = std.crypto.tls.Client.InitError(net.Stream);33 pub const TlsError = std.crypto.tls.Client.InitError(net.Stream);
52 pub const WriteError = BufferedConnection.WriteError;34 pub const WriteError = BufferedConnection.WriteError;
53 pub const ReadError = BufferedConnection.ReadError || error{HttpChunkInvalid};35 pub const ReadError = BufferedConnection.ReadError || error{HttpChunkInvalid};
54 pub const CaBundleError = impliedErrorSet(std.crypto.Certificate.Bundle.rescan);36 pub const CaBundleError = std.crypto.Certificate.Bundle.RescanError;
5537
56 pub const ZlibInitError = error{ BadHeader, InvalidCompression, InvalidWindowSize, Unsupported, EndOfStream, OutOfMemory } || Request.TransferReadError;38 pub const ZlibInitError = error{ BadHeader, InvalidCompression, InvalidWindowSize, Unsupported, EndOfStream, OutOfMemory } || Request.TransferReadError;
57 pub const GzipInitError = error{ BadHeader, InvalidCompression, OutOfMemory, WrongChecksum, EndOfStream, StreamTooLong } || Request.TransferReadError;39 pub const GzipInitError = error{ BadHeader, InvalidCompression, OutOfMemory, WrongChecksum, EndOfStream, StreamTooLong } || Request.TransferReadError;
58 // pub const DecompressError = Client.DeflateDecompressor.Error || Client.GzipDecompressor.Error || Client.ZstdDecompressor.Error;40 // pub const DecompressError = Compression.DeflateDecompressor.Error || Compression.GzipDecompressor.Error || Compression.ZstdDecompressor.Error;
59 pub const DecompressError = FakeDeflateDecompressor.Error || FakeGzipDecompressor.Error || FakeZstdDecompressor.Error;41 pub const DecompressError = anyerror; // FIXME: the above line causes a false positive dependency loop
6042
61 zlib_init: ZlibInitError, // error.CompressionInitializationFailed43 zlib_init: ZlibInitError, // error.CompressionInitializationFailed
62 gzip_init: GzipInitError, // error.CompressionInitializationFailed44 gzip_init: GzipInitError, // error.CompressionInitializationFailed