From effffbd1355799640ff749ff4a95202ed15faae0 Mon Sep 17 00:00:00 2001 From: Stephane Bortzmeyer Date: Sat, 2 Dec 2023 15:06:24 +0100 Subject: [PATCH 1/4] Limited implementation (no options) of EDNS --- src/dns.zig | 66 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/src/dns.zig b/src/dns.zig index d3f6237..6f6d598 100644 --- a/src/dns.zig +++ b/src/dns.zig @@ -52,6 +52,44 @@ pub fn createQuery(allocator: mem.Allocator, address: []const u8, qtype: QType) return message; } +pub const EDNS = struct { + bufsize: u16 = 1232, + do_dnssec: bool = true, + // In the future, EDNS options will come here + + pub fn format(self: *const EDNS, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { + _ = fmt; // We don't use these two parameters + _ = options; + try writer.print(" EDNS {{\n", .{}); + try writer.print(" Payload size: {d}\n", .{self.bufsize}); + try writer.print(" Do DNSSEC: {any}\n", .{self.do_dnssec}); + try writer.print(" }}\n", .{}); + } +}; + +pub fn createEDNSQuery(allocator: mem.Allocator, address: []const u8, qtype: QType, edns: EDNS) !Message { + var result = try createQuery(allocator, address, qtype); + result.header.additional_record_count += 1; + const domain = try DomainName.from_string(allocator, "."); + var flags: i32 = 0; // EDNS version 0 (RFC 6891) + if (edns.do_dnssec) { + flags = 0x8000; + } + var rr: ResourceRecord = .{ + .name = domain, + .type = Type.OPT, + .class = @enumFromInt(edns.bufsize), + .ttl = flags, + .resource_data_length = 0, // We do not yet handle EDNS options ({attribute,value} pairs) + .resource_data = ResourceData{.null = undefined}, + }; + var rrset = ResourceRecordList.init(allocator); + defer listDeinit(rrset); + try rrset.append(rr); + result.additional = try rrset.toOwnedSlice(); + return result; +} + /// DNS Message. All communications inside of the domain protocol are /// carried in a single format called a message. pub const Message = struct { @@ -59,6 +97,8 @@ pub const Message = struct { /// Contains information about the message. The header section is /// always present header: Header, + // EDNS information + edns: ?EDNS = null, /// The question(s) being asked in the query. This section usually /// contains one question. questions: []const Question, @@ -120,14 +160,25 @@ pub const Message = struct { errdefer listDeinit(additional); var add_idx: usize = 0; + var edns: ?EDNS = null; while (add_idx < header.additional_record_count) : (add_idx += 1) { const addit = try ResourceRecord.from_reader(allocator, reader); - try additional.append(addit); + if (addit.type == Type.OPT) { + const flags = addit.ttl; + const mask: u32 = 0x8000; + var do: bool = false; + if ((flags & mask) == mask) { + do = true; + } + edns = .{.bufsize = @intFromEnum(addit.class), .do_dnssec = do}; + } else { + try additional.append(addit); + } } - return Message{ .allocator = allocator, .header = header, + .edns = edns, .questions = try questions.toOwnedSlice(), .answers = try answers.toOwnedSlice(), .authorities = try authorities.toOwnedSlice(), @@ -176,6 +227,9 @@ pub const Message = struct { _ = options; try writer.print("Message {{\n", .{}); try writer.print("{any}", .{self.header}); + if (self.edns != null) { + try writer.print("{any}", .{self.edns}); + } try writer.print(" Questions {{\n", .{}); for (self.questions) |question| { try writer.print("{any}", .{question}); @@ -238,6 +292,7 @@ pub const Message = struct { const message = Message{ .allocator = self.allocator, .header = self.header, + .edns = self.edns, .questions = try questions.toOwnedSlice(), .answers = try answers.toOwnedSlice(), .authorities = try authorities.toOwnedSlice(), @@ -532,7 +587,7 @@ pub const ResourceRecord = struct { } }; -/// DNS Resource Record types +/// DNS Resource Record types https://www.iana.org/assignments/dns-parameters/dns-parameters.xml#dns-parameters-4 pub const Type = enum(u16) { /// A host address A = 1, @@ -574,6 +629,8 @@ pub const Type = enum(u16) { LOC = 29, /// Service locator SRV = 33, + // EDNS record + OPT = 41, /// SSH Fingerprint SSHFP = 44, /// Uniform Resource Identifier @@ -618,7 +675,8 @@ pub const Class = enum(u16) { CH = 3, /// Hesiod [Dyer 87] HS = 4, - + _, // Non-exhaustive enums for EDNS, where "class" is the payload size + pub fn format(self: Class, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { _ = fmt; _ = options; From 09798c292ee02eca12b52ceac1d6108a0f976787 Mon Sep 17 00:00:00 2001 From: Stephane Bortzmeyer Date: Sat, 2 Dec 2023 17:19:07 +0100 Subject: [PATCH 2/4] Packed struct? --- src/dns.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dns.zig b/src/dns.zig index 6f6d598..4de95d6 100644 --- a/src/dns.zig +++ b/src/dns.zig @@ -165,7 +165,7 @@ pub const Message = struct { const addit = try ResourceRecord.from_reader(allocator, reader); if (addit.type == Type.OPT) { const flags = addit.ttl; - const mask: u32 = 0x8000; + const mask: u32 = 0x8000; // Using a packed struct may be more ziggist. var do: bool = false; if ((flags & mask) == mask) { do = true; From 43df9a4cf69c900993a92a8f779bd3bb8f04566c Mon Sep 17 00:00:00 2001 From: Stephane Bortzmeyer Date: Thu, 21 Dec 2023 10:27:26 +0100 Subject: [PATCH 3/4] Now use packed structs instead of literal mask values --- src/dns.zig | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/dns.zig b/src/dns.zig index f4731db..cbdb825 100644 --- a/src/dns.zig +++ b/src/dns.zig @@ -56,7 +56,7 @@ pub const EDNS = struct { bufsize: u16 = 1232, do_dnssec: bool = true, // In the future, EDNS options will come here - + pub fn format(self: *const EDNS, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { _ = fmt; // We don't use these two parameters _ = options; @@ -67,26 +67,32 @@ pub const EDNS = struct { } }; +pub const PackedEDNSTTL = packed struct(u32) { + z2: u8 = 0, + z1: u7 = 0, // Zig put fields in the unexpected order inside a byte. + do: bool = true, + version: u8 = 0, // EDNS version 0 (RFC 6891) + extendedRcode: u8 = 0, +}; + pub fn createEDNSQuery(allocator: mem.Allocator, address: []const u8, qtype: QType, edns: EDNS) !Message { var result = try createQuery(allocator, address, qtype); result.header.additional_record_count += 1; const domain = try DomainName.from_string(allocator, "."); - var flags: i32 = 0; // EDNS version 0 (RFC 6891) - if (edns.do_dnssec) { - flags = 0x8000; - } - var rr: ResourceRecord = .{ + var flags = PackedEDNSTTL{}; + flags.do = edns.do_dnssec; + const rr: ResourceRecord = .{ .name = domain, .type = Type.OPT, .class = @enumFromInt(edns.bufsize), - .ttl = flags, + .ttl = @as(i32, @bitCast(flags)), .resource_data_length = 0, // We do not yet handle EDNS options ({attribute,value} pairs) - .resource_data = ResourceData{.null = undefined}, - }; + .resource_data = ResourceData{ .null = undefined }, + }; var rrset = ResourceRecordList.init(allocator); defer listDeinit(rrset); try rrset.append(rr); - result.additional = try rrset.toOwnedSlice(); + result.additional = try rrset.toOwnedSlice(); return result; } @@ -164,13 +170,9 @@ pub const Message = struct { while (add_idx < header.additional_record_count) : (add_idx += 1) { const addit = try ResourceRecord.from_reader(allocator, reader); if (addit.type == Type.OPT) { - const flags = addit.ttl; - const mask: u32 = 0x8000; // Using a packed struct may be more ziggist. - var do: bool = false; - if ((flags & mask) == mask) { - do = true; - } - edns = .{.bufsize = @intFromEnum(addit.class), .do_dnssec = do}; + const flags: PackedEDNSTTL = @as(PackedEDNSTTL, @bitCast(addit.ttl)); + const do: bool = flags.do; + edns = .{ .bufsize = @intFromEnum(addit.class), .do_dnssec = do }; } else { try additional.append(addit); } @@ -676,7 +678,7 @@ pub const Class = enum(u16) { /// Hesiod [Dyer 87] HS = 4, _, // Non-exhaustive enums for EDNS, where "class" is the payload size - + pub fn format(self: Class, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { _ = fmt; _ = options; From 33ad788b695374bae28797ec1d1526c7ff57c55a Mon Sep 17 00:00:00 2001 From: Stephane Bortzmeyer Date: Sat, 16 Mar 2024 08:59:53 +1000 Subject: [PATCH 4/4] The build system changed *again* --- build.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 701fdd6..8c6704d 100644 --- a/build.zig +++ b/build.zig @@ -10,8 +10,8 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); - const zig_network = b.createModule(.{ .source_file = .{ .path = "zig-network/network.zig" }}); - exe.addModule("network", zig_network); + const zig_network = b.createModule(.{ .root_source_file = .{ .path = "zig-network/network.zig" }}); + exe.root_module.addImport("network", zig_network); b.installArtifact(exe); const iter = b.addExecutable(.{ @@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); - iter.addModule("network", zig_network); + iter.root_module.addImport("network", zig_network); b.installArtifact(iter); const run_cmd = b.addRunArtifact(exe);