diff options
-rw-r--r-- | build.zig | 22 | ||||
-rw-r--r-- | src/Client.zig (renamed from src/saprus.zig) | 6 | ||||
-rw-r--r-- | src/main.zig | 35 | ||||
-rw-r--r-- | src/message.zig (renamed from src/saprus_message.zig) | 67 | ||||
-rw-r--r-- | src/root.zig | 2 |
5 files changed, 86 insertions, 46 deletions
@@ -15,6 +15,12 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); + const lib_mod = b.createModule(.{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }); + // We will also create a module for our other entry point, 'main.zig'. const exe_mod = b.createModule(.{ // `root_source_file` is the Zig "entry point" of the module. If a module @@ -26,7 +32,18 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); - exe_mod.addImport("network", b.dependency("network", .{}).module("network")); + lib_mod.addImport("network", b.dependency("network", .{}).module("network")); + + exe_mod.addImport("zaprus", lib_mod); + exe_mod.addImport("clap", b.dependency("clap", .{}).module("clap")); + + const lib = b.addLibrary(.{ + .linkage = .static, + .name = "zaprus", + .root_module = lib_mod, + }); + + b.installArtifact(lib); // This creates another `std.Build.Step.Compile`, but this one builds an executable // rather than a static library. @@ -35,9 +52,6 @@ pub fn build(b: *std.Build) void { .root_module = exe_mod, }); - const clap = b.dependency("clap", .{}); - exe.root_module.addImport("clap", clap.module("clap")); - // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default // step when running `zig build`). diff --git a/src/saprus.zig b/src/Client.zig index 4bc4af0..19f8360 100644 --- a/src/saprus.zig +++ b/src/Client.zig @@ -39,10 +39,10 @@ fn broadcastSaprusMessage(msg: SaprusMessage, udp_port: u16, allocator: Allocato _ = try sock.sendTo(dest_addr, msg_bytes); } -pub fn sendRelay(payload: []const u8, allocator: Allocator) !void { +pub fn sendRelay(payload: []const u8, dest: [4]u8, allocator: Allocator) !void { const msg = SaprusMessage{ .relay = .{ - .header = .{ .dest = .{ 255, 255, 255, 255 } }, + .header = .{ .dest = dest }, .payload = payload, }, }; @@ -112,7 +112,7 @@ pub fn connect(payload: []const u8, allocator: Allocator) !?SaprusMessage { return initial_conn_res; } -pub const SaprusMessage = @import("./saprus_message.zig").SaprusMessage; +const SaprusMessage = @import("message.zig").Message; const std = @import("std"); const Random = std.Random; diff --git a/src/main.zig b/src/main.zig index e425e56..f60614f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -23,6 +23,7 @@ pub fn main() !void { const params = comptime clap.parseParamsComptime( \\-h, --help Display this help and exit. \\-r, --relay <str> A relay message to send. + \\-d, --dest <str> An IPv4 or <= 4 ASCII byte string. \\-c, --connect <str> A connection message to send. \\ ); @@ -41,19 +42,24 @@ pub fn main() !void { }; defer res.deinit(); - try Saprus.init(); - defer Saprus.deinit(); + try SaprusClient.init(); + defer SaprusClient.deinit(); if (res.args.help != 0) { return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); } if (res.args.relay) |r| { - try Saprus.sendRelay(if (r.len > 0) r else "Hello darkness my old friend", gpa); - std.debug.print("Sent: {s}\n", .{r}); + const dest = parseDest(res.args.dest) catch .{ 70, 70, 70, 70 }; + try SaprusClient.sendRelay( + if (r.len > 0) r else "Hello darkness my old friend", + dest, + gpa, + ); + // std.debug.print("Sent: {s}\n", .{r}); return; } else if (res.args.connect) |c| { - const conn_res: ?SaprusMessage = Saprus.connect(if (c.len > 0) c else "Hello darkness my old friend", gpa) catch |err| switch (err) { + const conn_res: ?SaprusMessage = SaprusClient.connect(if (c.len > 0) c else "Hello darkness my old friend", gpa) catch |err| switch (err) { error.WouldBlock => null, else => return err, }; @@ -69,12 +75,27 @@ pub fn main() !void { return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); } +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.net.Ip4Address.parse(dest, 0) catch return "FAIL".*; + return @bitCast(addr.sa.addr); + } + return "zap".*; +} + const builtin = @import("builtin"); const std = @import("std"); const DebugAllocator = std.heap.DebugAllocator(.{}); const ArrayList = std.ArrayList; -const Saprus = @import("./saprus.zig"); -const SaprusMessage = Saprus.SaprusMessage; +const zaprus = @import("zaprus"); +const SaprusClient = zaprus.Client; +const SaprusMessage = zaprus.Message; const clap = @import("clap"); diff --git a/src/saprus_message.zig b/src/message.zig index 19ed58d..403bfcd 100644 --- a/src/saprus_message.zig +++ b/src/message.zig @@ -1,9 +1,9 @@ const base64Enc = std.base64.Base64Encoder.init(std.base64.standard_alphabet_chars, '='); const base64Dec = std.base64.Base64Decoder.init(std.base64.standard_alphabet_chars, '='); -/// Type tag for SaprusMessage union. +/// Type tag for Message union. /// This is the first value in the actual packet sent over the network. -pub const SaprusPacketType = enum(u16) { +pub const PacketType = enum(u16) { relay = 0x003C, file_transfer = 0x8888, connection = 0x00E9, @@ -12,7 +12,7 @@ pub const SaprusPacketType = enum(u16) { /// Reserved option values. /// Currently unused. -pub const SaprusConnectionOptions = packed struct(u8) { +pub const ConnectionOptions = packed struct(u8) { opt1: bool = false, opt2: bool = false, opt3: bool = false, @@ -23,13 +23,13 @@ pub const SaprusConnectionOptions = packed struct(u8) { opt8: bool = false, }; -pub const SaprusError = error{ +pub const Error = error{ NotImplementedSaprusType, UnknownSaprusType, }; /// All Saprus messages -pub const SaprusMessage = union(SaprusPacketType) { +pub const Message = union(PacketType) { pub const Relay = struct { pub const Header = packed struct { dest: @Vector(4, u8), @@ -44,7 +44,7 @@ pub const SaprusMessage = union(SaprusPacketType) { seq_num: u32 = 0, msg_id: u32 = 0, reserved: u8 = 0, - options: SaprusConnectionOptions = .{}, + options: ConnectionOptions = .{}, }; header: Header, payload: []const u8, @@ -53,8 +53,8 @@ pub const SaprusMessage = union(SaprusPacketType) { file_transfer: void, // unimplemented connection: Connection, - /// Should be called for any SaprusMessage that was declared using a function that you pass an allocator to. - pub fn deinit(self: SaprusMessage, allocator: Allocator) void { + /// Should be called for any Message that was declared using a function that you pass an allocator to. + pub fn deinit(self: Message, allocator: Allocator) void { switch (self) { .relay => |r| allocator.free(r.payload), .connection => |c| allocator.free(c.payload), @@ -96,7 +96,7 @@ pub const SaprusMessage = union(SaprusPacketType) { } /// Caller is responsible for freeing the returned bytes. - pub fn toBytes(self: SaprusMessage, allocator: Allocator) ![]u8 { + pub fn toBytes(self: Message, allocator: Allocator) ![]u8 { // Create a growable list of bytes to store the output in. var buf = std.ArrayList(u8).init(allocator); errdefer buf.deinit(); @@ -108,7 +108,7 @@ pub const SaprusMessage = union(SaprusPacketType) { switch (self) { .relay => |r| try toBytesAux(r.header, r.payload, &buf, allocator), .connection => |c| try toBytesAux(c.header, c.payload, &buf, allocator), - .file_transfer => return SaprusError.NotImplementedSaprusType, + .file_transfer => return Error.NotImplementedSaprusType, } // Collect the growable list as a slice and return it. @@ -116,13 +116,13 @@ pub const SaprusMessage = union(SaprusPacketType) { } fn fromBytesAux( - comptime packet: SaprusPacketType, + comptime packet: PacketType, + len: u16, r: std.io.FixedBufferStream([]const u8).Reader, allocator: Allocator, - ) !SaprusMessage { - const Header = @field(@FieldType(SaprusMessage, @tagName(packet)), "Header"); - // Read the length of the header + base64 encoded payload. - const len = try r.readInt(u16, .big); + ) !Message { + const Header = @field(@FieldType(Message, @tagName(packet)), "Header"); + // Read the header for the current message type. var header_bytes: [@sizeOf(Header)]u8 = undefined; _ = try r.read(header_bytes[0 .. @bitSizeOf(Header) / 8]); @@ -130,34 +130,37 @@ pub const SaprusMessage = union(SaprusPacketType) { const header = try header_stream.reader().readStructEndian(Header, .big); // Read the base64 bytes into a list to be able to call the decoder on it. - var payload_buf = std.ArrayList(u8).init(allocator); - defer payload_buf.deinit(); - try r.readAllArrayList(&payload_buf, len); + const payload_buf = try allocator.alloc(u8, len - @bitSizeOf(Header) / 8); + defer allocator.free(payload_buf); + _ = try r.readAll(payload_buf); // Create a buffer to store the payload in, and decode the base64 bytes into the payload field. - const payload = try allocator.alloc(u8, try base64Dec.calcSizeForSlice(payload_buf.items)); - try base64Dec.decode(payload, payload_buf.items); + const payload = try allocator.alloc(u8, try base64Dec.calcSizeForSlice(payload_buf)); + try base64Dec.decode(payload, payload_buf); - // Return the type of SaprusMessage specified by the `packet` argument. - return @unionInit(SaprusMessage, @tagName(packet), .{ + // Return the type of Message specified by the `packet` argument. + return @unionInit(Message, @tagName(packet), .{ .header = header, .payload = payload, }); } /// Caller is responsible for calling .deinit on the returned value. - pub fn fromBytes(bytes: []const u8, allocator: Allocator) !SaprusMessage { + pub fn fromBytes(bytes: []const u8, allocator: Allocator) !Message { var s = std.io.fixedBufferStream(bytes); const r = s.reader(); // Read packet type - const packet_type = @as(SaprusPacketType, @enumFromInt(try r.readInt(u16, .big))); + const packet_type = @as(PacketType, @enumFromInt(try r.readInt(u16, .big))); + + // Read the length of the header + base64 encoded payload. + const len = try r.readInt(u16, .big); switch (packet_type) { - .relay => return fromBytesAux(.relay, r, allocator), - .connection => return fromBytesAux(.connection, r, allocator), - .file_transfer => return SaprusError.NotImplementedSaprusType, - else => return SaprusError.UnknownSaprusType, + .relay => return fromBytesAux(.relay, len, r, allocator), + .connection => return fromBytesAux(.connection, len, r, allocator), + .file_transfer => return Error.NotImplementedSaprusType, + else => return Error.UnknownSaprusType, } } }; @@ -170,7 +173,7 @@ const nativeToBig = std.mem.nativeToBig; test "Round trip Relay toBytes and fromBytes" { const gpa = std.testing.allocator; - const msg = SaprusMessage{ + const msg = Message{ .relay = .{ .header = .{ .dest = .{ 255, 255, 255, 255 } }, .payload = "Hello darkness my old friend", @@ -180,7 +183,7 @@ test "Round trip Relay toBytes and fromBytes" { const to_bytes = try msg.toBytes(gpa); defer gpa.free(to_bytes); - const from_bytes = try SaprusMessage.fromBytes(to_bytes, gpa); + const from_bytes = try Message.fromBytes(to_bytes, gpa); defer from_bytes.deinit(gpa); try std.testing.expectEqualDeep(msg, from_bytes); @@ -188,7 +191,7 @@ test "Round trip Relay toBytes and fromBytes" { test "Round trip Connection toBytes and fromBytes" { const gpa = std.testing.allocator; - const msg = SaprusMessage{ + const msg = Message{ .connection = .{ .header = .{ .src_port = 0, @@ -201,7 +204,7 @@ test "Round trip Connection toBytes and fromBytes" { const to_bytes = try msg.toBytes(gpa); defer gpa.free(to_bytes); - const from_bytes = try SaprusMessage.fromBytes(to_bytes, gpa); + const from_bytes = try Message.fromBytes(to_bytes, gpa); defer from_bytes.deinit(gpa); try std.testing.expectEqualDeep(msg, from_bytes); diff --git a/src/root.zig b/src/root.zig new file mode 100644 index 0000000..5d93578 --- /dev/null +++ b/src/root.zig @@ -0,0 +1,2 @@ +pub const Client = @import("Client.zig"); +pub usingnamespace @import("message.zig"); |