-
Notifications
You must be signed in to change notification settings - Fork 0
fix(nsupdate): proper UPDATE response, serial consistency, TTL overflow guard #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -118,21 +118,82 @@ async fn handle_nsupdate_request(query_data: &[u8], client_addr: SocketAddr) -> | |||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| /// Returns the end offset of the first question (zone) section, or None if the message is invalid | ||||||||||||
| fn zone_section_end(message: &[u8]) -> Option<usize> { | ||||||||||||
| let mut offset = DNS_HEADER_LEN; | ||||||||||||
|
|
||||||||||||
| // Parse QNAME: labels ending with 0 or a compression pointer (2 bytes) | ||||||||||||
| loop { | ||||||||||||
| if offset >= message.len() { | ||||||||||||
| return None; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| let len = message[offset]; | ||||||||||||
|
|
||||||||||||
| if (len & 0xC0) == 0xC0 { | ||||||||||||
| // Compression pointer – two bytes, name ends here. | ||||||||||||
| if offset + 1 >= message.len() { | ||||||||||||
| return None; | ||||||||||||
| } | ||||||||||||
| offset += 2; | ||||||||||||
| break; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if len == 0 { | ||||||||||||
| // End of QNAME. | ||||||||||||
| offset += 1; | ||||||||||||
| break; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Regular label. | ||||||||||||
| offset += 1 + len as usize; | ||||||||||||
| if offset > message.len() { | ||||||||||||
| return None; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // QTYPE (2 bytes) + QCLASS (2 bytes). | ||||||||||||
| if offset + 4 > message.len() { | ||||||||||||
| return None; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| Some(offset + 4) | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| fn build_response(query_data: &[u8], rcode: u8) -> Option<Vec<u8>> { | ||||||||||||
| if query_data.len() < DNS_HEADER_LEN { | ||||||||||||
| return None; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| let mut response = vec![0u8; DNS_HEADER_LEN]; | ||||||||||||
| let opcode_bits = query_data[2] & 0x78; | ||||||||||||
| let rd_bit = query_data[2] & 0x01; | ||||||||||||
|
|
||||||||||||
| let qdcount = u16::from_be_bytes([query_data[4], query_data[5]]); | ||||||||||||
| let zone_end = if qdcount > 0 { | ||||||||||||
| zone_section_end(query_data) | ||||||||||||
| } else { | ||||||||||||
| None | ||||||||||||
| }; | ||||||||||||
|
Comment on lines
+171
to
+176
|
||||||||||||
|
|
||||||||||||
| let response_size = zone_end.unwrap_or(DNS_HEADER_LEN); | ||||||||||||
| let mut response = vec![0u8; response_size]; | ||||||||||||
|
|
||||||||||||
| // Transaction ID. | ||||||||||||
| response[0] = query_data[0]; | ||||||||||||
| response[1] = query_data[1]; | ||||||||||||
|
|
||||||||||||
| let opcode_bits = query_data[2] & 0x78; | ||||||||||||
| let rd_bit = query_data[2] & 0x01; | ||||||||||||
|
|
||||||||||||
| // QR=1, preserve opcode and RD bit. | ||||||||||||
| response[2] = 0x80 | opcode_bits | rd_bit; | ||||||||||||
| // Preserve upper flag nibble, set RCODE in lower nibble. | ||||||||||||
| response[3] = (query_data[3] & 0xF0) | (rcode & 0x0F); | ||||||||||||
|
Comment on lines
+187
to
188
|
||||||||||||
| // Preserve upper flag nibble, set RCODE in lower nibble. | |
| response[3] = (query_data[3] & 0xF0) | (rcode & 0x0F); | |
| // For UPDATE responses, do not mirror RA/AD/CD/Z bits from the query. | |
| // Explicitly set the upper nibble to 0 and only encode the RCODE. | |
| response[3] = rcode & 0x0F; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zone_section_endtreats any length byte that isn't0, a normal label (00xxxxxx), or a compression pointer (11xxxxxx) as a “regular label”. Values with01xxxxxx/10xxxxxxare reserved label types in DNS wire format; accepting them can cause the function to parse/echo invalid names and potentially allocate/copy unexpectedly large slices. Consider rejecting those reserved forms (returnNone) when(len & 0xC0) != 0 && (len & 0xC0) != 0xC0.