mirror of
https://git.robbyzambito.me/zaprus
synced 2025-12-20 16:24:50 +00:00
The slice sets us avoid allocating within the init function. This means init can't fail, and it also makes it easier to stack allocate messages (slice an array buffer, instead of creating a stack allocator).
149 lines
4.2 KiB
Zig
149 lines
4.2 KiB
Zig
const base64Enc = std.base64.Base64Encoder.init(std.base64.standard_alphabet_chars, '=');
|
|
const base64Dec = std.base64.Base64Decoder.init(std.base64.standard_alphabet_chars, '=');
|
|
|
|
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) !void {
|
|
if (false) {
|
|
var foo: gcat.nic.RawSocket = try .init("enp7s0"); // /proc/net/dev
|
|
defer foo.deinit();
|
|
}
|
|
const msg_bytes = msg.asBytes();
|
|
try msg.networkFromNativeEndian();
|
|
defer msg.nativeFromNetworkEndian() catch unreachable;
|
|
|
|
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, dest: [4]u8, allocator: Allocator) !void {
|
|
const msg_bytes = try allocator.alignedAlloc(
|
|
u8,
|
|
@alignOf(SaprusMessage),
|
|
try SaprusMessage.lengthForPayloadLength(
|
|
.relay,
|
|
base64Enc.calcSize(payload.len),
|
|
),
|
|
);
|
|
defer allocator.free(msg_bytes);
|
|
const msg: *SaprusMessage = .init(.relay, msg_bytes);
|
|
|
|
const relay = (try msg.getSaprusTypePayload()).relay;
|
|
relay.dest = dest;
|
|
_ = base64Enc.encode(relay.getPayload(), payload);
|
|
|
|
try broadcastSaprusMessage(msg, 8888);
|
|
}
|
|
|
|
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_bytes = try allocator.alignedAlloc(
|
|
u8,
|
|
@alignOf(SaprusMessage),
|
|
try SaprusMessage.lengthForPayloadLength(.connection, payload.len),
|
|
);
|
|
defer allocator.free(msg_bytes);
|
|
const msg: *SaprusMessage = .init(.connection, msg_bytes);
|
|
|
|
const connection = (try msg.getSaprusTypePayload()).connection;
|
|
connection.src_port = initial_port;
|
|
connection.dest_port = dest_port;
|
|
@memcpy(connection.getPayload(), payload);
|
|
|
|
try broadcastSaprusMessage(msg, 8888);
|
|
|
|
return msg;
|
|
}
|
|
|
|
pub fn connect(payload: []const u8, allocator: Allocator) !?SaprusConnection {
|
|
var initial_port: u16 = 0;
|
|
if (rand) |r| {
|
|
initial_port = r.intRangeAtMost(u16, 1024, 65000);
|
|
} else unreachable;
|
|
|
|
var initial_conn_res: ?*SaprusMessage = null;
|
|
|
|
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 align(4) = @splat(0);
|
|
_ = try sock.receive(&response_buf); // Ignore message that I sent.
|
|
const len = try sock.receive(&response_buf);
|
|
|
|
std.debug.print("response bytes: {x}\n", .{response_buf[0..len]});
|
|
initial_conn_res = SaprusMessage.init(.connection, response_buf[0..len]);
|
|
|
|
// Complete handshake after awaiting response
|
|
try broadcastSaprusMessage(msg, randomPort());
|
|
|
|
if (false) {
|
|
return initial_conn_res.?;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const SaprusMessage = @import("message.zig").Message;
|
|
const SaprusConnection = @import("Connection.zig");
|
|
|
|
const std = @import("std");
|
|
const Random = std.Random;
|
|
const posix = std.posix;
|
|
const mem = std.mem;
|
|
|
|
const network = @import("network");
|
|
const gcat = @import("gatorcat");
|
|
|
|
const Allocator = mem.Allocator;
|