1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
use crate::{read_u16_be, read_u8, write_u16_be, DnsError, DnsOpCode, DnsResponseCode};
use fixed_buffer::FixedBuf;
/// > 4.1.1. Header section format
/// >
/// > The header contains the following fields:
/// >
/// > ```text
/// > 1 1 1 1 1 1
/// > 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// > | ID |
/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// > |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// > | QDCOUNT |
/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// > | ANCOUNT |
/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// > | NSCOUNT |
/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// > | ARCOUNT |
/// > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// > ```
///
/// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
#[allow(clippy::struct_excessive_bools)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct DnsMessageHeader {
/// > `ID` A 16 bit identifier assigned by the program that generates any kind of query. This
/// > identifier is copied the corresponding reply and can be used by the requester to match up
/// > replies to outstanding queries.
///
/// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
pub id: u16,
/// > `QR` A one bit field that specifies whether this message is a query (`0`),
/// > or a response (`1`).
///
/// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
pub is_response: bool,
/// > `OPCODE` A four bit field that specifies kind of query in this message.
/// > This value is set by the originator of a query and copied into
/// > the response. The values are:
/// > - `0` a standard query (`QUERY`)
/// > - `1` an inverse query (`IQUERY`)
/// > - `2` a server status request (`STATUS`)
/// > - `3-15` reserved for future use
///
/// https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1
pub op_code: DnsOpCode,
/// > `AA` Authoritative Answer - this bit is valid in responses, and specifies that the
/// > responding name server is an authority for the domain name in question section.
/// >
/// > Note that the contents of the answer section may have multiple owner names because of
/// > aliases. The AA bit corresponds to the name which matches the query name, or the first
/// > owner name in the answer section.
///
/// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
pub authoritative_answer: bool,
/// > `TC` TrunCation - specifies that this message was truncated due to length greater than
/// > that permitted on the transmission channel.
///
/// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
pub truncated: bool,
/// > `RD` Recursion Desired - this bit may be set in a query and is copied into the response.
/// > If RD is set, it directs the name server to pursue the query recursively. Recursive query
/// > support is optional.
///
/// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
pub recursion_desired: bool,
/// > `RA` Recursion Available - this be is set or cleared in a response, and denotes whether
/// > recursive query support is available in the name server.
///
/// <https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1>
pub recursion_available: bool,
pub response_code: DnsResponseCode,
pub question_count: u16,
pub answer_count: u16,
pub name_server_count: u16,
pub additional_count: u16,
}
impl DnsMessageHeader {
/// # Errors
/// Returns an error when `buf` does not contain a valid message header.
pub fn read<const N: usize>(buf: &mut FixedBuf<N>) -> Result<Self, DnsError> {
let id = read_u16_be(buf)?;
let b = read_u8(buf)?;
let is_response = (b >> 7) == 1;
let op_code = DnsOpCode::new((b >> 3) & 0xF);
let authoritative_answer = ((b >> 2) & 1) == 1;
let truncated = ((b >> 1) & 1) == 1;
let recursion_desired = (b & 1) == 1;
let b = read_u8(buf)?;
let recursion_available = (b >> 7) == 1;
let response_code = DnsResponseCode::new(b & 0xF);
let question_count = read_u16_be(buf)?;
let answer_count = read_u16_be(buf)?;
let name_server_count = read_u16_be(buf)?;
let additional_count = read_u16_be(buf)?;
Ok(Self {
id,
is_response,
op_code,
authoritative_answer,
truncated,
recursion_desired,
recursion_available,
response_code,
question_count,
answer_count,
name_server_count,
additional_count,
})
}
/// # Errors
/// Returns an error when `buf` fills up.
pub fn write<const N: usize>(&self, out: &mut FixedBuf<N>) -> Result<(), DnsError> {
let bytes: [u8; 2] = self.id.to_be_bytes();
out.write_bytes(&bytes)
.map_err(|_| DnsError::ResponseBufferFull)?;
let b = (u8::from(self.is_response) << 7)
| (self.op_code.num() << 3)
| (u8::from(self.authoritative_answer) << 2)
| (u8::from(self.truncated) << 1)
| u8::from(self.recursion_desired);
out.write_bytes(&[b])
.map_err(|_| DnsError::ResponseBufferFull)?;
let b = (u8::from(self.recursion_available) << 7) | self.response_code.num();
out.write_bytes(&[b])
.map_err(|_| DnsError::ResponseBufferFull)?;
for count in [
self.question_count,
self.answer_count,
self.name_server_count,
self.additional_count,
] {
write_u16_be(out, count)?;
}
Ok(())
}
}