diff options
Diffstat (limited to 'src/Connection.zig')
| -rw-r--r-- | src/Connection.zig | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/src/Connection.zig b/src/Connection.zig new file mode 100644 index 0000000..19be710 --- /dev/null +++ b/src/Connection.zig @@ -0,0 +1,105 @@ +// Copyright 2026 Robby Zambito +// +// This file is part of zaprus. +// +// Zaprus is free software: you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// Zaprus is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// Zaprus. If not, see <https://www.gnu.org/licenses/>. + +socket: RawSocket, +headers: EthIpUdp, +connection: SaprusMessage, + +const Connection = @This(); + +// 'p' as base64 +const pong = "cA=="; + +/// Attempts to read from the network, and returns the next message, if any. +/// +/// Asserts that `buf` is large enough to store the message that is received. +/// +/// This will internally process management messages, and return the message +/// payload for the next non management connection message. +/// This function is ignorant to the message encoding. +pub fn next(self: *Connection, io: Io, buf: []u8) ![]const u8 { + while (true) { + log.debug("Awaiting connection message", .{}); + const res = try self.socket.receive(buf); + log.debug("Received {} byte connection message", .{res.len}); + const msg = SaprusMessage.parse(res[42..]) catch |err| { + log.err("Failed to parse next message: {t}\n{x}\n{x}", .{ err, res[0..], res[42..] }); + return err; + }; + + switch (msg) { + .connection => |con_res| { + if (try con_res.management()) |mgt| { + log.debug("Received management message {t}", .{mgt}); + switch (mgt) { + .ping => { + log.debug("Sending pong", .{}); + try self.send(io, .{ .management = true }, pong); + log.debug("Sent pong message", .{}); + }, + else => |m| log.debug("Received management message that I don't know how to handle: {t}", .{m}), + } + } else { + log.debug("Payload was {s}", .{con_res.payload}); + return con_res.payload; + } + }, + else => |m| { + std.debug.panic("Expected connection message, instead got {x}. This means there is an error with the BPF.", .{@intFromEnum(m)}); + }, + } + } +} + +/// Attempts to write a message to the network. +/// +/// Clients should pass `.{}` for options unless you know what you are doing. +/// `buf` will be sent over the network as-is; this function is ignorant of encoding. +pub fn send(self: *Connection, io: Io, options: SaprusMessage.Connection.Options, buf: []const u8) !void { + const io_source: std.Random.IoSource = .{ .io = io }; + const rand = io_source.interface(); + + log.debug("Sending connection message", .{}); + + self.connection.connection.options = options; + self.connection.connection.payload = buf; + var connection_bytes_buf: [2048]u8 = undefined; + const connection_bytes = self.connection.toBytes(&connection_bytes_buf); + + self.headers.ip.id = rand.int(u16); + self.headers.setPayloadLen(connection_bytes.len); + + var msg_buf: [2048]u8 = undefined; + var msg_w: Writer = .fixed(&msg_buf); + try msg_w.writeAll(&self.headers.toBytes()); + try msg_w.writeAll(connection_bytes); + const full_msg = msg_w.buffered(); + + try self.socket.send(full_msg); + + log.debug("Sent {} byte connection message", .{full_msg.len}); +} + +const std = @import("std"); +const Io = std.Io; +const Writer = std.Io.Writer; + +const log = std.log; + +const SaprusMessage = @import("./message.zig").Message; + +const EthIpUdp = @import("./EthIpUdp.zig").EthIpUdp; +const RawSocket = @import("./RawSocket.zig"); |
