diff --git a/src/app.zig b/src/app.zig index 7cce8bbe..5c90b546 100644 --- a/src/app.zig +++ b/src/app.zig @@ -17,11 +17,18 @@ pub const App = struct { app_dir_path: ?[]const u8, pub const RunMode = enum { - serve, + help, fetch, + serve, + version, }; - pub fn init(allocator: Allocator, run_mode: RunMode) !*App { + pub const Config = struct { + tls_verify_host: bool = true, + run_mode: RunMode, + }; + + pub fn init(allocator: Allocator, config: Config) !*App { const app = try allocator.create(App); errdefer allocator.destroy(app); @@ -38,9 +45,11 @@ pub const App = struct { .allocator = allocator, .telemetry = undefined, .app_dir_path = app_dir_path, - .http_client = try HttpClient.init(allocator, 5), + .http_client = try HttpClient.init(allocator, 5, .{ + .tls_verify_host = config.tls_verify_host, + }), }; - app.telemetry = Telemetry.init(app, run_mode); + app.telemetry = Telemetry.init(app, config.run_mode); return app; } diff --git a/src/cdp/testing.zig b/src/cdp/testing.zig index 68f26014..891fc67f 100644 --- a/src/cdp/testing.zig +++ b/src/cdp/testing.zig @@ -281,7 +281,7 @@ const TestContext = struct { pub fn context() TestContext { return .{ - .app = App.init(std.testing.allocator, .serve) catch unreachable, + .app = App.init(std.testing.allocator, .{ .run_mode = .serve }) catch unreachable, .arena = std.heap.ArenaAllocator.init(std.testing.allocator), }; } diff --git a/src/http/client.zig b/src/http/client.zig index b0192287..4669a3e0 100644 --- a/src/http/client.zig +++ b/src/http/client.zig @@ -45,9 +45,13 @@ pub const Client = struct { allocator: Allocator, state_pool: StatePool, root_ca: tls.config.CertBundle, + tls_verify_host: bool = true, - // we only allow passing in a root_ca for testing - pub fn init(allocator: Allocator, max_concurrent: usize) !Client { + const Opts = struct { + tls_verify_host: bool = true, + }; + + pub fn init(allocator: Allocator, max_concurrent: usize, opts: Opts) !Client { var root_ca = try tls.config.CertBundle.fromSystem(allocator); errdefer root_ca.deinit(allocator); @@ -58,6 +62,7 @@ pub const Client = struct { .root_ca = root_ca, .allocator = allocator, .state_pool = state_pool, + .tls_verify_host = opts.tls_verify_host, }; } @@ -123,7 +128,7 @@ pub const Request = struct { _has_host_header: bool, // Whether or not we should verify that the host matches the certificate CN - _tls_verify_host: bool = true, + _tls_verify_host: bool, pub const Method = enum { GET, @@ -167,6 +172,7 @@ pub const Request = struct { ._client = client, ._redirect_count = 0, ._has_host_header = false, + ._tls_verify_host = client.tls_verify_host, }; } @@ -205,11 +211,13 @@ pub const Request = struct { // TODO timeout const SendSyncOpts = struct { - tls_verify_host: bool = true, + tls_verify_host: ?bool = null, }; // Makes an synchronous request pub fn sendSync(self: *Request, opts: SendSyncOpts) anyerror!Response { - self._tls_verify_host = opts.tls_verify_host; + if (opts.tls_verify_host) |override| { + self._tls_verify_host = override; + } try self.prepareInitialSend(); return self.doSendSync(); } @@ -230,11 +238,13 @@ pub const Request = struct { } const SendAsyncOpts = struct { - tls_verify_host: bool = true, + tls_verify_host: ?bool = null, }; // Makes an asynchronous request pub fn sendAsync(self: *Request, loop: anytype, handler: anytype, opts: SendAsyncOpts) !void { - self._tls_verify_host = opts.tls_verify_host; + if (opts.tls_verify_host) |override| { + self._tls_verify_host = override; + } try self.prepareInitialSend(); return self.doSendAsync(loop, handler); } @@ -2176,5 +2186,5 @@ fn testReader(state: *State, res: *TestResponse, data: []const u8) !void { } fn testClient() !Client { - return try Client.init(testing.allocator, 1); + return try Client.init(testing.allocator, 1, .{}); } diff --git a/src/main.zig b/src/main.zig index f18ec2a5..6e7a6526 100644 --- a/src/main.zig +++ b/src/main.zig @@ -22,6 +22,7 @@ const Allocator = std.mem.Allocator; const jsruntime = @import("jsruntime"); +const App = @import("app.zig").App; const Browser = @import("browser/browser.zig").Browser; const server = @import("server.zig"); @@ -69,9 +70,12 @@ pub fn main() !void { log.err("address (host:port) {any}\n", .{err}); return args.printUsageAndExit(false); }; - - var app = try @import("app.zig").App.init(alloc, .serve); + var app = try App.init(alloc, .{ + .run_mode = args.mode, + .tls_verify_host = opts.tls_verify_host, + }); defer app.deinit(); + app.telemetry.record(.{ .run = {} }); const timeout = std.time.ns_per_s * @as(u64, opts.timeout); @@ -83,7 +87,10 @@ pub fn main() !void { .fetch => |opts| { log.debug("Fetch mode: url {s}, dump {any}", .{ opts.url, opts.dump }); - var app = try @import("app.zig").App.init(alloc, .fetch); + var app = try App.init(alloc, .{ + .run_mode = args.mode, + .tls_verify_host = opts.tls_verify_host, + }); defer app.deinit(); app.telemetry.record(.{ .run = {} }); @@ -125,14 +132,7 @@ const Command = struct { mode: Mode, exec_name: []const u8, - const ModeType = enum { - help, - fetch, - serve, - version, - }; - - const Mode = union(ModeType) { + const Mode = union(App.RunMode) { help: bool, // false when being printed because of an error fetch: Fetch, serve: Serve, @@ -143,11 +143,13 @@ const Command = struct { host: []const u8, port: u16, timeout: u16, + tls_verify_host: bool, }; const Fetch = struct { url: []const u8, dump: bool = false, + tls_verify_host: bool, }; fn printUsageAndExit(self: *const Command, success: bool) void { @@ -164,6 +166,12 @@ const Command = struct { \\--dump Dumps document to stdout. \\ Defaults to false. \\ + \\--insecure_disable_tls_host_verification + \\ Disables host verification on all HTTP requests. + \\ This is an advanced option which should only be + \\ set if you understand and accept the risk of + \\ disabling host verification. + \\ \\serve command \\Starts a websocket CDP server \\Example: {s} serve --host 127.0.0.1 --port 9222 @@ -178,11 +186,18 @@ const Command = struct { \\--timeout Inactivity timeout in seconds before disconnecting clients \\ Defaults to 3 (seconds) \\ + \\--insecure_disable_tls_host_verification + \\ Disables host verification on all HTTP requests. + \\ This is an advanced option which should only be + \\ set if you understand and accept the risk of + \\ disabling host verification. + \\ \\version command \\Displays the version of {s} \\ \\help command \\Displays this message + \\ ; std.debug.print(usage, .{ self.exec_name, self.exec_name, self.exec_name, self.exec_name }); if (success) { @@ -204,7 +219,7 @@ fn parseArgs(allocator: Allocator) !Command { }; const mode_string = args.next() orelse ""; - const mode = std.meta.stringToEnum(Command.ModeType, mode_string) orelse blk: { + const mode = std.meta.stringToEnum(App.RunMode, mode_string) orelse blk: { const inferred_mode = inferMode(mode_string) orelse return cmd; // "command" wasn't a command but an option. We can't reset args, but // we can create a new one. Not great, but this fallback is temporary @@ -227,7 +242,7 @@ fn parseArgs(allocator: Allocator) !Command { return cmd; } -fn inferMode(opt: []const u8) ?Command.ModeType { +fn inferMode(opt: []const u8) ?App.RunMode { if (opt.len == 0) { return .serve; } @@ -260,6 +275,7 @@ fn parseServeArgs( var host: []const u8 = "127.0.0.1"; var port: u16 = 9222; var timeout: u16 = 3; + var tls_verify_host = true; while (args.next()) |opt| { if (std.mem.eql(u8, "--host", opt)) { @@ -297,6 +313,11 @@ fn parseServeArgs( continue; } + if (std.mem.eql(u8, "--insecure_tls_verify_host", opt)) { + tls_verify_host = false; + continue; + } + log.err("Unknown option to serve command: '{s}'", .{opt}); return error.UnkownOption; } @@ -305,6 +326,7 @@ fn parseServeArgs( .host = host, .port = port, .timeout = timeout, + .tls_verify_host = tls_verify_host, }; } @@ -314,6 +336,7 @@ fn parseFetchArgs( ) !Command.Fetch { var dump: bool = false; var url: ?[]const u8 = null; + var tls_verify_host = true; while (args.next()) |opt| { if (std.mem.eql(u8, "--dump", opt)) { @@ -321,6 +344,11 @@ fn parseFetchArgs( continue; } + if (std.mem.eql(u8, "--insecure_disable_tls_host_verification", opt)) { + tls_verify_host = false; + continue; + } + if (std.mem.startsWith(u8, opt, "--")) { log.err("Unknown option to serve command: '{s}'", .{opt}); return error.UnkownOption; @@ -338,7 +366,11 @@ fn parseFetchArgs( return error.MissingURL; } - return .{ .url = url.?, .dump = dump }; + return .{ + .url = url.?, + .dump = dump, + .tls_verify_host = tls_verify_host, + }; } var verbose: bool = builtin.mode == .Debug; // In debug mode, force verbose. diff --git a/src/main_tests.zig b/src/main_tests.zig index 41560b99..29bba51c 100644 --- a/src/main_tests.zig +++ b/src/main_tests.zig @@ -88,7 +88,7 @@ fn testExecFn( std.debug.print("documentHTMLClose error: {s}\n", .{@errorName(err)}); }; - var http_client = try @import("http/client.zig").Client.init(alloc, 5); + var http_client = try @import("http/client.zig").Client.init(alloc, 5, .{}); defer http_client.deinit(); try js_env.setUserContext(.{ diff --git a/src/main_unit_tests.zig b/src/main_unit_tests.zig index caecc188..9937f0b8 100644 --- a/src/main_unit_tests.zig +++ b/src/main_unit_tests.zig @@ -213,7 +213,7 @@ fn serveHTTPS(address: std.net.Address) !void { fn serveCDP(address: std.net.Address) !void { const App = @import("app.zig").App; - var app = try App.init(gpa.allocator(), .serve); + var app = try App.init(gpa.allocator(), .{ .run_mode = .serve }); defer app.deinit(); const server = @import("server.zig"); diff --git a/src/testing.zig b/src/testing.zig index 8df7b31a..7e157f7e 100644 --- a/src/testing.zig +++ b/src/testing.zig @@ -157,7 +157,7 @@ pub fn print(comptime fmt: []const u8, args: anytype) void { // dummy opts incase we want to add something, and not have to break all the callers pub fn app(_: anytype) *App { - return App.init(allocator, .serve) catch unreachable; + return App.init(allocator, .{ .run_mode = .serve }) catch unreachable; } pub const Random = struct { diff --git a/src/wpt/run.zig b/src/wpt/run.zig index 8677188b..9989e36b 100644 --- a/src/wpt/run.zig +++ b/src/wpt/run.zig @@ -55,7 +55,7 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const var loop = try Loop.init(alloc); defer loop.deinit(); - var http_client = try HttpClient.init(alloc, 2); + var http_client = try HttpClient.init(alloc, 2, .{}); defer http_client.deinit(); var js_env: Env = undefined;