//! By convention, main.zig is where your main function lives in the case that //! you are building an executable. If you are making a library, the convention //! is to delete this file and start with root.zig instead. const is_debug = builtin.mode == .Debug; const SaprusPacketType = enum(u16) { relay = 0x003C, file_transfer = 0x8888, }; const SaprusHeaderFrame = struct { msg_type: SaprusPacketType, payload: []u8, const Self = @This(); fn toBytes(s: Self, allocator: Allocator) ![]u8 { const buf = try allocator.alloc(u8, 32 + s.payload.len); std.mem.writeInt(u16, buf[0..2], @intFromEnum(s.msg_type), .big); std.mem.writeInt(u16, buf[2..4], @intCast(s.payload.len), .big); std.mem.copyForwards(u8, buf[4..], s.payload); return buf; } }; const SaprusRelayMessage = struct { dest: [4]u8, payload: []u8, const Self = @This(); fn toBytes(s: Self, allocator: Allocator) ![]u8 { const buf = try allocator.alloc(u8, 4 + s.payload.len); std.mem.copyForwards(u8, buf[0..4], &s.dest); std.mem.copyForwards(u8, buf[4..], s.payload); return buf; } }; pub fn main() !void { const DBA = std.heap.DebugAllocator(.{}); var dba: ?DBA = if (comptime is_debug) DBA.init else null; defer if (dba) |*d| { _ = d.deinit(); }; var allocator = if (dba) |*d| d.allocator() else std.heap.smp_allocator; const relay: SaprusRelayMessage = .{ .dest = [_]u8{ 255, 255, 255, 255 }, .payload = @ptrCast(@constCast("Hello darkness my old friend")), }; const relay_bytes = try relay.toBytes(allocator); defer allocator.free(relay_bytes); const msg: SaprusHeaderFrame = .{ .msg_type = .relay, .payload = relay_bytes, }; const msg_bytes = try msg.toBytes(allocator); defer allocator.free(msg_bytes); try network.init(); defer network.deinit(); var sock = try network.Socket.create(.ipv4, .udp); defer sock.close(); try sock.setBroadcast(true); // Bind to 0.0.0.0:0 const bind_addr = network.EndPoint{ .address = network.Address{ .ipv4 = network.Address.IPv4.any }, .port = 0, }; const dest_addr = network.EndPoint{ .address = network.Address{ .ipv4 = network.Address.IPv4.broadcast }, .port = 8888, }; try sock.bind(bind_addr); // @breakpoint(); _ = try sock.sendTo(dest_addr, msg_bytes); // // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) // std.debug.print("All your {any} are belong to us.\n", .{msg}); // // stdout is for the actual output of your application, for example if you // // are implementing gzip, then only the compressed bytes should be sent to // // stdout, not any debugging messages. // const stdout_file = std.io.getStdOut().writer(); // var bw = std.io.bufferedWriter(stdout_file); // const stdout = bw.writer(); // try stdout.print("Run `zig build test` to run the tests.\n", .{}); // try bw.flush(); // Don't forget to flush! } test "simple test" { var list = std.ArrayList(i32).init(std.testing.allocator); defer list.deinit(); // Try commenting this out and see if zig detects the memory leak! try list.append(42); try std.testing.expectEqual(@as(i32, 42), list.pop()); } test "fuzz example" { const Context = struct { fn testOne(context: @This(), input: []const u8) anyerror!void { _ = context; // Try passing `--fuzz` to `zig build test` and see if it manages to fail this test case! try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input)); } }; try std.testing.fuzz(Context{}, Context.testOne, .{}); } const builtin = @import("builtin"); const std = @import("std"); const Allocator = std.mem.Allocator; const network = @import("network");