diff options
Diffstat (limited to 'src/Client.zig')
-rw-r--r-- | src/Client.zig | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/src/Client.zig b/src/Client.zig new file mode 100644 index 0000000..8f60326 --- /dev/null +++ b/src/Client.zig @@ -0,0 +1,124 @@ +var rand: ?Random = null; + +pub fn init() !void { + var prng = Random.DefaultPrng.init(blk: { + var seed: u64 = undefined; + try posix.getrandom(mem.asBytes(&seed)); + break :blk seed; + }); + rand = prng.random(); + try network.init(); +} + +pub fn deinit() void { + network.deinit(); +} + +fn broadcastSaprusMessage(msg: SaprusMessage, udp_port: u16, allocator: Allocator) !void { + const msg_bytes = try msg.toBytes(allocator); + defer allocator.free(msg_bytes); + + 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 = udp_port, + }; + + try sock.bind(bind_addr); + + _ = try sock.sendTo(dest_addr, msg_bytes); +} + +pub fn sendRelay(payload: []const u8, allocator: Allocator) !void { + const msg = SaprusMessage{ + .relay = .{ + .header = .{ .dest = .{ 70, 70, 70, 70 } }, + .payload = payload, + }, + }; + + try broadcastSaprusMessage(msg, 8888, allocator); +} + +fn randomPort() u16 { + var p: u16 = 0; + if (rand) |r| { + p = r.intRangeAtMost(u16, 1024, 65000); + } else unreachable; + + return p; +} + +pub fn sendInitialConnection(payload: []const u8, initial_port: u16, allocator: Allocator) !SaprusMessage { + const dest_port = randomPort(); + const msg = SaprusMessage{ + .connection = .{ + .header = .{ + .src_port = initial_port, + .dest_port = dest_port, + }, + .payload = payload, + }, + }; + + try broadcastSaprusMessage(msg, 8888, allocator); + + return msg; +} + +pub fn connect(payload: []const u8, allocator: Allocator) !?SaprusMessage { + var initial_port: u16 = 0; + if (rand) |r| { + initial_port = r.intRangeAtMost(u16, 1024, 65000); + } else unreachable; + + var initial_conn_res: ?SaprusMessage = null; + errdefer if (initial_conn_res) |c| c.deinit(allocator); + + var sock = try network.Socket.create(.ipv4, .udp); + defer sock.close(); + + // Bind to 255.255.255.255:8888 + const bind_addr = network.EndPoint{ + .address = network.Address{ .ipv4 = network.Address.IPv4.broadcast }, + .port = 8888, + }; + + // timeout 1s + try sock.setReadTimeout(1 * std.time.us_per_s); + try sock.bind(bind_addr); + + const msg = try sendInitialConnection(payload, initial_port, allocator); + + var response_buf: [4096]u8 = undefined; + _ = try sock.receive(&response_buf); // Ignore message that I sent. + const len = try sock.receive(&response_buf); + + initial_conn_res = try SaprusMessage.fromBytes(response_buf[0..len], allocator); + + // Complete handshake after awaiting response + try broadcastSaprusMessage(msg, randomPort(), allocator); + + return initial_conn_res; +} + +const SaprusMessage = @import("message.zig").Message; + +const std = @import("std"); +const Random = std.Random; +const posix = std.posix; +const mem = std.mem; + +const network = @import("network"); + +const Allocator = mem.Allocator; |