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
signature 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" {
371371 try expectEqual(false, Parsed.checkHostName("lang.org", "zig*.org"));
372372}
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 {
375377 const cert_bytes = cert.buffer;
376378 const certificate = try der.Element.parse(cert_bytes, cert.index);
377379 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 {
514516 return cert.buffer[elem.slice.start..elem.slice.end];
515517}
516518
519pub const ParseBitStringError = error{ CertificateFieldHasWrongDataType, CertificateHasInvalidBitString };
520
517521pub fn parseBitString(cert: Certificate, elem: der.Element) !der.Element.Slice {
518522 if (elem.identifier.tag != .bitstring) return error.CertificateFieldHasWrongDataType;
519523 if (cert.buffer[elem.slice.start] != 0) return error.CertificateHasInvalidBitString;
520524 return .{ .start = elem.slice.start + 1, .end = elem.slice.end };
521525}
522526
527pub const ParseTimeError = error{ CertificateTimeInvalid, CertificateFieldHasWrongDataType };
528
523529/// 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 {
525531 const bytes = cert.contents(elem);
526532 switch (elem.identifier.tag) {
527533 .utc_time => {
......@@ -647,34 +653,38 @@ test parseYear4 {
647653 try expectError(error.CertificateTimeInvalid, parseYear4("crap"));
648654}
649655
650pub fn parseAlgorithm(bytes: []const u8, element: der.Element) !Algorithm {
656pub fn parseAlgorithm(bytes: []const u8, element: der.Element) ParseEnumError!Algorithm {
651657 return parseEnum(Algorithm, bytes, element);
652658}
653659
654pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) !AlgorithmCategory {
660pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) ParseEnumError!AlgorithmCategory {
655661 return parseEnum(AlgorithmCategory, bytes, element);
656662}
657663
658pub fn parseAttribute(bytes: []const u8, element: der.Element) !Attribute {
664pub fn parseAttribute(bytes: []const u8, element: der.Element) ParseEnumError!Attribute {
659665 return parseEnum(Attribute, bytes, element);
660666}
661667
662pub fn parseNamedCurve(bytes: []const u8, element: der.Element) !NamedCurve {
668pub fn parseNamedCurve(bytes: []const u8, element: der.Element) ParseEnumError!NamedCurve {
663669 return parseEnum(NamedCurve, bytes, element);
664670}
665671
666pub fn parseExtensionId(bytes: []const u8, element: der.Element) !ExtensionId {
672pub fn parseExtensionId(bytes: []const u8, element: der.Element) ParseEnumError!ExtensionId {
667673 return parseEnum(ExtensionId, bytes, element);
668674}
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 {
671679 if (element.identifier.tag != .object_identifier)
672680 return error.CertificateFieldHasWrongDataType;
673681 const oid_bytes = bytes[element.slice.start..element.slice.end];
674682 return E.map.get(oid_bytes) orelse return error.CertificateHasUnrecognizedObjectId;
675683}
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 {
678688 if (@bitCast(u8, version_elem.identifier) != 0xa0)
679689 return .v1;
680690
......@@ -861,9 +871,9 @@ pub const der = struct {
861871 pub const empty: Slice = .{ .start = 0, .end = 0 };
862872 };
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 {
867877 var i = index;
868878 const identifier = @bitCast(Identifier, bytes[i]);
869879 i += 1;
lib/std/crypto/Certificate/Bundle.zig+27-10
......@@ -50,11 +50,13 @@ pub fn deinit(cb: *Bundle, gpa: Allocator) void {
5050 cb.* = undefined;
5151}
5252
53pub const RescanError = RescanLinuxError || RescanMacError || RescanWindowsError;
54
5355/// Clears the set of certificates and then scans the host operating system
5456/// file system standard locations for certificates.
5557/// For operating systems that do not have standard CA installations to be
5658/// 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 {
5860 switch (builtin.os.tag) {
5961 .linux => return rescanLinux(cb, gpa),
6062 .macos => return rescanMac(cb, gpa),
......@@ -64,8 +66,11 @@ pub fn rescan(cb: *Bundle, gpa: Allocator) !void {
6466}
6567
6668pub 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 {
6974 // Possible certificate files; stop after finding one.
7075 const cert_file_paths = [_][]const u8{
7176 "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
......@@ -107,7 +112,9 @@ pub fn rescanLinux(cb: *Bundle, gpa: Allocator) !void {
107112 cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
108113}
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 {
111118 cb.bytes.clearRetainingCapacity();
112119 cb.map.clearRetainingCapacity();
113120
......@@ -132,12 +139,14 @@ pub fn rescanWindows(cb: *Bundle, gpa: Allocator) !void {
132139 cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
133140}
134141
142pub const AddCertsFromDirPathError = fs.File.OpenError || AddCertsFromDirError;
143
135144pub fn addCertsFromDirPath(
136145 cb: *Bundle,
137146 gpa: Allocator,
138147 dir: fs.Dir,
139148 sub_dir_path: []const u8,
140) !void {
149) AddCertsFromDirPathError!void {
141150 var iterable_dir = try dir.openIterableDir(sub_dir_path, .{});
142151 defer iterable_dir.close();
143152 return addCertsFromDir(cb, gpa, iterable_dir);
......@@ -147,14 +156,16 @@ pub fn addCertsFromDirPathAbsolute(
147156 cb: *Bundle,
148157 gpa: Allocator,
149158 abs_dir_path: []const u8,
150) !void {
159) AddCertsFromDirPathError!void {
151160 assert(fs.path.isAbsolute(abs_dir_path));
152161 var iterable_dir = try fs.openIterableDirAbsolute(abs_dir_path, .{});
153162 defer iterable_dir.close();
154163 return addCertsFromDir(cb, gpa, iterable_dir);
155164}
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 {
158169 var it = iterable_dir.iterate();
159170 while (try it.next()) |entry| {
160171 switch (entry.kind) {
......@@ -166,11 +177,13 @@ pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.IterableDir
166177 }
167178}
168179
180pub const AddCertsFromFilePathError = fs.File.OpenError || AddCertsFromFileError;
181
169182pub fn addCertsFromFilePathAbsolute(
170183 cb: *Bundle,
171184 gpa: Allocator,
172185 abs_file_path: []const u8,
173) !void {
186) AddCertsFromFilePathError!void {
174187 assert(fs.path.isAbsolute(abs_file_path));
175188 var file = try fs.openFileAbsolute(abs_file_path, .{});
176189 defer file.close();
......@@ -182,13 +195,15 @@ pub fn addCertsFromFilePath(
182195 gpa: Allocator,
183196 dir: fs.Dir,
184197 sub_file_path: []const u8,
185) !void {
198) AddCertsFromFilePathError!void {
186199 var file = try dir.openFile(sub_file_path, .{});
187200 defer file.close();
188201 return addCertsFromFile(cb, gpa, file);
189202}
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 {
192207 const size = try file.getEndPos();
193208
194209 // 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 {
222237 }
223238}
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 {
226243 // Even though we could only partially parse the certificate to find
227244 // the subject name, we pre-parse all of them to make sure and only
228245 // 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;
55const Allocator = std.mem.Allocator;
66const 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 {
911 cb.bytes.clearRetainingCapacity();
1012 cb.map.clearRetainingCapacity();
1113
lib/std/http/Client.zig+3-21
......@@ -29,34 +29,16 @@ connection_pool: ConnectionPool = .{},
2929last_error: ?ExtraError = null,
3030
3131pub 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
5032 pub const TcpConnectError = std.net.TcpConnectToHostError;
5133 pub const TlsError = std.crypto.tls.Client.InitError(net.Stream);
5234 pub const WriteError = BufferedConnection.WriteError;
5335 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
5638 pub const ZlibInitError = error{ BadHeader, InvalidCompression, InvalidWindowSize, Unsupported, EndOfStream, OutOfMemory } || Request.TransferReadError;
5739 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;
59 pub const DecompressError = FakeDeflateDecompressor.Error || FakeGzipDecompressor.Error || FakeZstdDecompressor.Error;
40 // pub const DecompressError = Compression.DeflateDecompressor.Error || Compression.GzipDecompressor.Error || Compression.ZstdDecompressor.Error;
41 pub const DecompressError = anyerror; // FIXME: the above line causes a false positive dependency loop
6042
6143 zlib_init: ZlibInitError, // error.CompressionInitializationFailed
6244 gzip_init: GzipInitError, // error.CompressionInitializationFailed