@@ -88,10 +88,7 @@ pub const Peer = struct {
pub const Message = struct {
header: Header,
questions: ?[]Question = null,
answers: ?[]Resource = null,
authorities: ?[]Resource = null,
additionals: ?[]Resource = null,
bytes: []const u8,
pub const Header = packed struct(u96) {
arcount: u16,
@@ -140,12 +137,17 @@ pub const Message = struct {
}
};
pub const Payload = union(enum) {
question: Question,
answer: Resource,
};
pub const Question = struct {
name: []const u8,
qtype: Type,
class: Class,
pub fn init(name: []const u8) !Question {
pub fn init(name: []const u8) Question {
return .{
.name = name,
.qtype = .a,
@@ -153,11 +155,13 @@ pub const Message = struct {
};
}
pub fn write(q: Question, w: *std.io.AnyWriter) !void {
pub fn write(q: Question, w: *std.io.AnyWriter) !usize {
try Label.writeName(q.name, w);
if (q.name[q.name.len - 1] != '.') try w.writeByte(0);
const fqdn: bool = q.name[q.name.len - 1] == '.';
if (!fqdn) try w.writeByte(0);
try w.writeInt(u16, @intFromEnum(q.qtype), .big);
try w.writeInt(u16, @intFromEnum(q.class), .big);
return q.name.len + if (fqdn) 5 else @as(u8, 6);
}
};
@@ -206,61 +210,68 @@ pub const Message = struct {
_,
};
pub fn fromBytes(a: Allocator, bytes: []const u8) !Message {
pub fn fromBytes(bytes: []const u8) !Message {
if (bytes.len < 12) return error.MessageTooSmall;
const header: Header = .fromBytes(bytes[0..12].*);
log.warn("{}", .{header});
var idx: usize = 12;
const questions = try a.alloc(Question, header.qdcount);
for (questions) |*q| {
const name = try Label.getName(a, bytes, &idx);
defer a.free(name);
log.warn("label name {s}", .{name});
q.* = .{
.name = name,
.qtype = @enumFromInt(@byteSwap(@as(u16, @bitCast(bytes[idx..][0..2].*)))),
.class = @enumFromInt(@byteSwap(@as(u16, @bitCast(bytes[idx..][2..4].*)))),
};
log.warn("{any}", .{q.*});
idx += 4;
}
const resources = try a.alloc(Resource, header.ancount);
for (resources) |*r| {
log.warn("{} {}", .{ idx, bytes[idx] });
const name = try Label.getName(a, bytes, &idx);
defer a.free(name);
log.warn("{s}", .{name});
const rdlen: u16 = @byteSwap(@as(u16, @bitCast(bytes[idx..][8..10].*)));
r.* = .{
.name = name,
.rtype = @enumFromInt(@byteSwap(@as(u16, @bitCast(bytes[idx..][0..2].*)))),
.class = @enumFromInt(@byteSwap(@as(u16, @bitCast(bytes[idx..][2..4].*)))),
.ttl = @byteSwap(@as(u32, @bitCast(bytes[idx..][4..8].*))),
.rdlength = rdlen,
.rdata = bytes[idx..][10..][0..rdlen],
};
log.warn("{any}", .{r.*});
if (r.*.rtype != .a) @panic("not implemented");
}
return .{
.header = header,
.questions = questions,
.answers = resources,
.bytes = bytes,
};
}
pub fn query(a: Allocator, fqdn: []const []const u8) !Message {
const queries = try a.alloc(Question, fqdn.len);
for (queries, fqdn) |*q, dn| {
q.* = try .init(dn);
}
pub fn payload(msg: Message, index: usize) !?Payload {
const payload_end = msg.header.qdcount + msg.header.ancount +
msg.header.nscount + msg.header.arcount;
if (index >= payload_end) return error.InvalidIndex;
// TODO only byteswap when endian changes
return .{
var name_buf: [128]u8 = undefined;
var idx: usize = 12;
for (0..payload_end) |payload_idx| {
if (payload_idx < msg.header.qdcount) {
const name = try Label.getName(&name_buf, msg.bytes, &idx);
log.warn("label name {s}", .{name});
if (payload_idx == index) {
return .{ .question = .{
.name = name,
.qtype = @enumFromInt(@byteSwap(@as(u16, @bitCast(msg.bytes[idx..][0..2].*)))),
.class = @enumFromInt(@byteSwap(@as(u16, @bitCast(msg.bytes[idx..][2..4].*)))),
} };
} else {
_ = try Label.getName(&name_buf, msg.bytes, &idx);
}
//log.warn("{any}", .{q.*});
} else {
if (payload_idx == index) {
log.warn("{} {}", .{ idx, msg.bytes[idx] });
const name = try Label.getName(&name_buf, msg.bytes, &idx);
log.warn("{s}", .{name});
const rdlen: u16 = @byteSwap(@as(u16, @bitCast(msg.bytes[idx..][8..10].*)));
if (idx == index) {
const r: Resource = .{
.name = name,
.rtype = @enumFromInt(@byteSwap(@as(u16, @bitCast(msg.bytes[idx..][0..2].*)))),
.class = @enumFromInt(@byteSwap(@as(u16, @bitCast(msg.bytes[idx..][2..4].*)))),
.ttl = @byteSwap(@as(u32, @bitCast(msg.bytes[idx..][4..8].*))),
.rdlength = rdlen,
.rdata = msg.bytes[idx..][10..][0..rdlen],
};
if (r.rtype != .a) @panic("not implemented");
return .{ .resource = r };
} else {
_ = try Label.getName(&name_buf, msg.bytes, &idx);
idx += @byteSwap(@as(u16, @bitCast(msg.bytes[idx..][8..10].*)));
}
}
}
}
}
pub fn query(fqdns: []const []const u8, buffer: []u8) !Message {
var msg: Message = .{
.header = .{
.id = @as(u16, 31337),
.qr = false,
@@ -270,13 +281,24 @@ pub const Message = struct {
.rd = true,
.ra = false,
.rcode = .success,
.qdcount = @as(u16, @truncate(queries.len)),
.qdcount = @as(u16, @truncate(fqdns.len)),
.ancount = 0,
.nscount = 0,
.arcount = 0,
},
.questions = queries,
.bytes = buffer,
};
var fbs = std.io.fixedBufferStream(buffer);
var writer = fbs.writer();
var w = writer.any();
var idx = try msg.write(&w);
for (fqdns) |fqdn| {
const q: Question = .init(fqdn);
idx += try q.write(&w);
}
msg.bytes.len = idx;
return msg;
}
pub fn answer(domain: []const u8, ip: Address) !Message {
@@ -288,41 +310,19 @@ pub const Message = struct {
_ = domain;
}
pub fn write(m: Message, buffer: []u8) !usize {
var fbs = std.io.fixedBufferStream(buffer);
var writer = fbs.writer();
var w = writer.any();
pub fn write(m: Message, w: *std.io.AnyWriter) !usize {
try w.writeInt(u96, @bitCast(m.header), .big);
if (m.questions) |quest| for (quest) |q| {
try q.write(&w);
};
if (m.answers) |ans| for (ans) |a| {
a.write();
};
if (m.authorities) |authort| for (authort) |a| {
a.write();
};
if (m.additionals) |addit| for (addit) |a| {
a.write();
};
return fbs.pos;
std.debug.assert(@sizeOf(u96) >= 12);
return 12;
}
test query {
const q = try query(std.testing.allocator, &[1][]const u8{"gr.ht."});
var buffer: [23]u8 = undefined;
const q = try query(&[1][]const u8{"gr.ht."}, &buffer);
try std.testing.expectEqual(
@as(u96, 37884113131630398792389361664),
@as(u96, @bitCast(q.header)),
);
for (q.questions.?) |qst| {
_ = qst;
//std.testing.allocator.free(qst.name);
}
std.testing.allocator.free(q.questions.?);
}
};
@@ -349,14 +349,18 @@ pub const Label = struct {
return label;
}
pub fn getName(a: Allocator, bytes: []const u8, index: *usize) ![]u8 {
var name = try std.ArrayListUnmanaged(u8).initCapacity(a, 285);
pub fn getName(buffer: []u8, bytes: []const u8, index: *usize) ![]u8 {
var name: std.ArrayListUnmanaged(u8) = .{
.items = buffer,
.capacity = buffer.len,
};
name.items.len = 0;
var idx: usize = index.*;
var pointered: bool = false;
sw: switch (bytes[idx]) {
0 => {
if (!pointered) index.* = idx + 1;
return try name.toOwnedSlice(a);
return try name.items;
},
1...63 => |b| {
idx += b + 1;
@@ -422,22 +426,10 @@ test "Message.Header" {
);
}
fn testVector(a: Allocator, vect: []const u8) !void {
const msg = try Message.fromBytes(a, vect);
try std.testing.expectEqual(1, msg.questions.?.len);
try std.testing.expectEqual(1, msg.answers.?.len);
a.free(msg.questions.?);
a.free(msg.answers.?);
}
test "build pkt" {
const a = std.testing.allocator;
const msg = try Message.query(a, &[1][]const u8{"gr.ht."});
var buffer: [23]u8 = undefined;
const used = try msg.write(&buffer);
a.free(msg.questions.?);
try std.testing.expect(used == 23);
const msg = try Message.query(&[1][]const u8{"gr.ht."}, &buffer);
try std.testing.expectEqual(msg.bytes.len, 23);
try std.testing.expectEqualSlices(
u8,
@@ -450,13 +442,9 @@ test "build pkt" {
}
test "build pkt non-fqdn" {
const a = std.testing.allocator;
const msg = try Message.query(a, &[1][]const u8{"gr.ht"});
var buffer: [23]u8 = undefined;
const used = try msg.write(&buffer);
a.free(msg.questions.?);
try std.testing.expect(used == 23);
const msg = try Message.query(&[1][]const u8{"gr.ht"}, &buffer);
try std.testing.expectEqual(msg.bytes.len, 23);
try std.testing.expectEqualSlices(
u8,
@@ -469,35 +457,39 @@ test "build pkt non-fqdn" {
}
test "grht vectors" {
const a = std.testing.allocator;
try testVector(a, &[_]u8{
//const a = std.testing.allocator;
const msg0 = try Message.fromBytes(&[_]u8{
122, 105, 129, 128, 0, 1, 0, 1, 0, 0, 0, 0,
2, 103, 114, 2, 104, 116, 0, 0, 1, 0, 1, 192,
12, 0, 1, 0, 1, 0, 0, 14, 16, 0, 4, 127,
4, 20, 69,
});
try std.testing.expectEqual(msg0.header.qdcount, 1);
try std.testing.expectEqual(msg0.header.ancount, 1);
//try testVector(a, &[_]u8{
// 122, 105, 129, 131, 0, 1, 0, 0, 0, 1, 0, 0, 2, 64,
// 49, 1, 49, 1, 49, 1, 49, 0, 0, 1, 0, 1, 0, 0,
// 6, 0, 1, 0, 1, 81, 128, 0, 64, 1, 97, 12, 114, 111,
// 111, 116, 45, 115, 101, 114, 118, 101, 114, 115, 3, 110, 101, 116,
// 0, 5, 110, 115, 116, 108, 100, 12, 118, 101, 114, 105, 115, 105,
// 103, 110, 45, 103, 114, 115, 3, 99, 111, 109, 0, 120, 179, 132,
// 44, 0, 0, 7, 8, 0, 0, 3, 132, 0, 9, 58, 128, 0,
// 1, 81, 128,
//});
const msg1 = try Message.fromBytes(&[_]u8{
122, 105, 129, 131, 0, 1, 0, 0, 0, 1, 0, 0, 2, 64,
49, 1, 49, 1, 49, 1, 49, 0, 0, 1, 0, 1, 0, 0,
6, 0, 1, 0, 1, 81, 128, 0, 64, 1, 97, 12, 114, 111,
111, 116, 45, 115, 101, 114, 118, 101, 114, 115, 3, 110, 101, 116,
0, 5, 110, 115, 116, 108, 100, 12, 118, 101, 114, 105, 115, 105,
103, 110, 45, 103, 114, 115, 3, 99, 111, 109, 0, 120, 179, 132,
44, 0, 0, 7, 8, 0, 0, 3, 132, 0, 9, 58, 128, 0,
1, 81, 128,
});
try std.testing.expectEqual(msg1.header.qdcount, 1);
try std.testing.expectEqual(msg1.header.ancount, 0);
}
test "simple test" {}
test "fuzz example" {
const global = struct {
fn testOne(input: []const u8) anyerror!void {
try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input));
}
};
try std.testing.fuzz(global.testOne, .{});
//const global = struct {
// fn testOne(input: []const u8) anyerror!void {
// try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input));
// }
//};
//try std.testing.fuzz(global.testOne, .{});
}
const std = @import("std");