mirror of
https://git.robbyzambito.me/zaprus/
synced 2026-02-04 11:44:49 +00:00
195 lines
5.6 KiB
Zig
195 lines
5.6 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;
|
|
var socket: ?gcat.nic.RawSocket = null;
|
|
|
|
const max_message_size = 2048;
|
|
|
|
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();
|
|
|
|
socket = try .init("enp7s0");
|
|
}
|
|
|
|
pub fn deinit() void {
|
|
socket.?.deinit();
|
|
}
|
|
|
|
/// Used for relay messages and connection handshake.
|
|
/// Assumes Client .init has been called.
|
|
fn broadcastInitialInterestMessage(msg_bytes: []align(@alignOf(SaprusMessage)) u8) !void {
|
|
var packet_bytes: [max_message_size]u8 = comptime blk: {
|
|
var b: [max_message_size]u8 = @splat(0);
|
|
|
|
// Destination MAC addr to FF:FF:FF:FF:FF:FF
|
|
for (0..6) |i| {
|
|
b[i] = 0xff;
|
|
}
|
|
|
|
// Set Ethernet type to IPv4
|
|
b[0x0c] = 0x08;
|
|
b[0x0d] = 0x00;
|
|
|
|
// Set IPv4 version to 4
|
|
b[0x0e] = 0x45;
|
|
|
|
// Destination broadcast
|
|
for (0x1e..0x22) |i| {
|
|
b[i] = 0xff;
|
|
}
|
|
|
|
// Set TTL
|
|
b[0x16] = 0x40;
|
|
|
|
// Set IPv4 protocol to UDP
|
|
b[0x17] = 0x11;
|
|
|
|
// Set interest filter value to 8888.
|
|
b[0x24] = 0x22;
|
|
b[0x25] = 0xb8;
|
|
break :blk b;
|
|
};
|
|
var msg: *SaprusMessage = try .bytesAsValue(msg_bytes);
|
|
try msg.networkFromNativeEndian();
|
|
defer msg.nativeFromNetworkEndian() catch unreachable;
|
|
|
|
// The byte position within the packet that the saprus message starts at.
|
|
const saprus_start_byte = 42;
|
|
@memcpy(packet_bytes[saprus_start_byte .. saprus_start_byte + msg_bytes.len], msg_bytes);
|
|
|
|
try socket.?.linkLayer().send(packet_bytes[0 .. saprus_start_byte + msg_bytes.len]);
|
|
}
|
|
|
|
// fn broadcastSaprusMessage(msg_bytes: []align(@alignOf(SaprusMessage)) u8) !void {}
|
|
|
|
fn broadcastSaprusMessage(msg_bytes: []align(@alignOf(SaprusMessage)) u8, udp_port: u16) !void {
|
|
if (false) {
|
|
var foo: gcat.nic.RawSocket = try .init("enp7s0"); // /proc/net/dev
|
|
defer foo.deinit();
|
|
}
|
|
const msg: *SaprusMessage = try .bytesAsValue(msg_bytes);
|
|
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);
|
|
|
|
std.debug.print("{x}\n", .{msg_bytes});
|
|
|
|
_ = try sock.sendTo(dest_addr, msg_bytes);
|
|
}
|
|
|
|
pub fn sendRelay(payload: []const u8, dest: [4]u8) !void {
|
|
var buf: [max_message_size]u8 align(@alignOf(SaprusMessage)) = undefined;
|
|
const msg_bytes = buf[0..try SaprusMessage.calcSize(
|
|
.relay,
|
|
base64Enc.calcSize(payload.len),
|
|
)];
|
|
const msg: *SaprusMessage = .init(.relay, msg_bytes);
|
|
|
|
const relay = (try msg.getSaprusTypePayload()).relay;
|
|
relay.dest = dest;
|
|
_ = base64Enc.encode(relay.getPayload(), payload);
|
|
|
|
try broadcastInitialInterestMessage(msg_bytes);
|
|
}
|
|
|
|
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,
|
|
output_bytes: []align(@alignOf(SaprusMessage)) u8,
|
|
initial_port: u16,
|
|
) !*SaprusMessage {
|
|
const dest_port = randomPort();
|
|
const msg_bytes = output_bytes[0..try SaprusMessage.calcSize(
|
|
.connection,
|
|
base64Enc.calcSize(payload.len),
|
|
)];
|
|
const msg: *SaprusMessage = .init(.connection, msg_bytes);
|
|
|
|
const connection = (try msg.getSaprusTypePayload()).connection;
|
|
connection.src_port = initial_port;
|
|
connection.dest_port = dest_port;
|
|
_ = base64Enc.encode(connection.getPayload(), payload);
|
|
|
|
try broadcastSaprusMessage(msg_bytes, 8888);
|
|
|
|
return msg;
|
|
}
|
|
|
|
pub fn connect(payload: []const u8) !?SaprusConnection {
|
|
const initial_port = randomPort();
|
|
|
|
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);
|
|
|
|
var sent_msg_bytes: [max_message_size]u8 align(@alignOf(SaprusMessage)) = undefined;
|
|
const msg = try sendInitialConnection(payload, &sent_msg_bytes, initial_port);
|
|
|
|
var response_buf: [max_message_size]u8 align(@alignOf(SaprusMessage)) = undefined;
|
|
_ = try sock.receive(&response_buf); // Ignore message that I sent.
|
|
const len = try sock.receive(&response_buf);
|
|
|
|
initial_conn_res = try .networkBytesAsValue(response_buf[0..len]);
|
|
|
|
// Complete handshake after awaiting response
|
|
try broadcastSaprusMessage(msg.asBytes(), 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");
|