summaryrefslogtreecommitdiff
path: root/src/Client.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Client.zig')
-rw-r--r--src/Client.zig124
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;