this repo has no description
1const std = @import("std");
2
3pub const BytePacketBuffer = struct {
4 buf: [512]u8 = undefined,
5 pos: usize = 0,
6
7 /// Change the buffer position forward a specific number of steps
8 pub fn step(self: *BytePacketBuffer, pos: usize) void {
9 self.pos += pos;
10 }
11
12 /// Chanke the buffer position
13 pub fn seek(self: *BytePacketBuffer, pos: usize) void {
14 self.pos = pos;
15 }
16
17 /// Read a single byte and move the position one step forward
18 pub fn read(self: *BytePacketBuffer) error{EndOfBuffer}!u8 {
19 if (self.pos >= comptime self.buf.len) return error.EndOfBuffer;
20 const res = self.buf[self.pos];
21 self.pos += 1;
22 return res;
23 }
24
25 /// Get a single byte without changing the buffer position
26 pub fn get(self: *const BytePacketBuffer, pos: usize) error{EndOfBuffer}!u8 {
27 if (pos >= comptime self.buf.len) return error.EndOfBuffer;
28 return self.buf[pos];
29 }
30
31 /// Get a range of bytes
32 pub fn get_range(self: *const BytePacketBuffer, start: usize, len: usize) error{EndOfBuffer}![]const u8 {
33 if (start + len >= comptime self.buf.len) return error.EndOfBuffer;
34 return self.buf[start .. start + len];
35 }
36
37 /// Read two bytes, stepping two steps forward
38 pub fn read_u16(self: *BytePacketBuffer) error{EndOfBuffer}!u16 {
39 return (@as(u16, try self.read()) << 8) |
40 @as(u16, try self.read());
41 }
42
43 /// Read two bytes, stepping two steps forward
44 pub fn read_u32(self: *BytePacketBuffer) error{EndOfBuffer}!u32 {
45 return @as(u32, try self.read()) << 24 |
46 (@as(u32, try self.read()) << 16) |
47 (@as(u32, try self.read()) << 8) |
48 (@as(u32, try self.read()));
49 }
50
51 /// Read a qname
52 ///
53 /// The tricky part: Reading domain names, taking labels into consideration.
54 /// Will take something like [3]www[6]google[3]com and append
55 /// www.google.com to outstr.
56 pub fn read_qname(self: *BytePacketBuffer, outstr: []u8) !void {
57 // We might encounter jumps, therefore we need to keep thrack of our position locally
58 var pos = self.pos;
59 var out_pos: usize = 0;
60
61 // track whether or nor we've jumped
62 var jumped = false;
63 const max_jumps: usize = 5;
64 var jumps_performed: usize = 0;
65
66 var delim: ?[]const u8 = null;
67 while (true) {
68 if (jumps_performed > max_jumps) return error.JumpLimitExceeded;
69
70 // Each label starts with a length byte
71 const len = try self.get(pos);
72
73 // If len has the two most signigicant bit set, it represents a jump to some other
74 // offset in the packet:
75 if ((len & 0xC0) == 0xC0) {
76 // Update the buffer position to a point past the current label
77 if (!jumped) self.seek(2);
78
79 // Read another byte, calculate offset and performe the jump by updating our
80 // local position variable
81 const b2 = @as(u16, try self.get(pos + 1));
82 const offset = ((@as(u16, len) ^ 0xC0) << 8) | b2;
83 pos = @as(usize, offset);
84
85 // Indicate that a jump was performed
86 jumped = true;
87 jumps_performed += 1;
88
89 continue;
90 } else {
91 // Move a single byte forward to move path the length
92 pos += 1;
93
94 // Domain names are terminated by an empty label of length 0, so if the length
95 // is zero we're done
96 if (len == 0) break;
97
98 if (delim) |del| {
99 @memcpy(outstr[out_pos .. out_pos + del.len], del);
100 out_pos += del.len;
101 }
102
103 @memcpy(outstr[out_pos .. out_pos + len], try self.get_range(pos, len));
104 delim = ".";
105
106 pos += len;
107 out_pos += len;
108 }
109 }
110
111 if (!jumped) self.seek(1);
112 }
113};
114
115test "BytePacketBuffer.read" {
116 const testing = std.testing;
117 var buf = BytePacketBuffer{};
118 buf.buf[0] = 0x1;
119 try testing.expectEqual(0x1, try buf.read());
120}
121
122test "BytePacketBuffer.read_u16" {
123 const testing = std.testing;
124 var buf = BytePacketBuffer{};
125 buf.buf[0] = 0x1;
126 buf.buf[1] = 0x1;
127 try testing.expectEqual(0x101, try buf.read_u16());
128}
129
130test "BytePacketBuffer.read_u32" {
131 const testing = std.testing;
132 var buf = BytePacketBuffer{};
133 buf.buf[0] = 0x1;
134 buf.buf[1] = 0x1;
135 buf.buf[2] = 0x1;
136 buf.buf[3] = 0x1;
137 try testing.expectEqual(0x1010101, try buf.read_u32());
138}
139
140test "BytePacketBuffer.read_qname" {
141 const testing = std.testing;
142 const allocator = testing.allocator;
143
144 const input = [_]u8{ 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00 };
145 var buf = BytePacketBuffer{};
146
147 for (input, 0..) |char, idx| {
148 buf.buf[idx] = char;
149 }
150
151 const expected = "google.com";
152 const outstr = try allocator.alloc(u8, expected.len);
153 defer allocator.free(outstr);
154
155 try buf.read_qname(outstr);
156
157 try testing.expectEqualStrings(expected, outstr);
158}