-
Notifications
You must be signed in to change notification settings - Fork 21
Small fix to ddns update and unit test to verify. #73
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
Conversation
|
Thank you for your work testing and fixing the issue! |
| let mut prerequisite = Record::update0(name.clone(), 0, RecordType::ANY); | ||
| prerequisite.set_dns_class(DNSClass::NONE); | ||
| message.add_update(prerequisite); | ||
| message.add_pre_requisite(prerequisite); |
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.
You said this was a regression, was this previously add_prerequisite and your PR changed to add_update?
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.
Ok I think I understand the issue. The original code added the dhcid record to prereq, it was supposed to be update. Your first fix changed both to update but only dhcid record is meant to be in that section
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.
I think so, yes. I've read the RFC a couple times and I think this is the way it's should be. Originally I had this as an add_answer which is the same thing as far as the bytes over the wire are concerned but this is more clear from a spec point of view.
For what it's worth I swear the previous version also worked but this maybe more correct.
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.
afaik the prereq section is effectively the condition that's required and the update is the action that's going to happen.
It's possible it worked with both in that case. Not having any conditions on the update action. But I think the name here is supposed to be the condition.
It's possible the same is the case for dhcid, it could go in either. In one case it means only update if dhcid matches (same device is renewing). In the other case it means add the dhcid along with the a/aaaa record, so it's like making a new entry for the client?
But it's been a while since I looked at the RFC
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.
I think the same change needs to be made in update_present to change the add_update back to add_prerequisite?
we've got this in update_present
let mut prerequisite = Record::update0(name.clone(), 0, RecordType::ANY);
// use ANY to check only update if this name is present
prerequisite.set_dns_class(DNSClass::ANY);
message.add_update(prerequisite);
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.
I don't think I've read the RFC parts that deal with the dhcpid resolution. What do you think about adding some more options to the configuration dns updates? I can see having forward/reverse being a selection as well as the dhcpid inclusion or not being something that's configurable.
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.
I think the same change needs to be made in update_present to change the add_update back to add_prerequisite? we've got this in update_present
let mut prerequisite = Record::update0(name.clone(), 0, RecordType::ANY); // use ANY to check only update if this name is present prerequisite.set_dns_class(DNSClass::ANY); message.add_update(prerequisite);
I think that is likely. I couldn't explicitly reproduce the behavior in my test setup. I did the delete/create behavior but I didn't wait for the refresh cycle to happen to see what I think would trigger this behavior? I could try that next and create another PR for that.
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.
https://datatracker.ietf.org/doc/html/rfc4703#section-5.3.2
I'm just checking this part out. I think the correct flow for the conflict resolution (update_present) is this:
// Prerequisite 1: name exists
let mut prerequisite = Record::update0(name.clone(), 0, RecordType::ANY);
prerequisite.set_dns_class(DNSClass::ANY);
message.add_prerequisite(prerequisite); // wrong
...
// Prerequisite 2: DHCID matches
let dhcid_record = Record::from_rdata(...);
message.add_prerequisite(dhcid_record); // wrong
...
// Update: add A record
let a_record = Record::from_rdata(name, ttl, A(leased).into_rdata());
message.add_update(a_record);
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.
If you're interested in pursuing this, that would be great. I can revert update_present to it's previous state and then the question would be if that dhcid belongs in prereq or update in the case of a conflict. (can be done in another PR)
|
Just for fun I asked copilot to review |
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.
Pull request overview
This PR fixes a regression in DDNS (Dynamic DNS) updates where a prerequisite record was incorrectly added to the update section instead of the prerequisite section. The fix ensures DDNS UPDATE messages conform to RFC 2136 and work correctly with Kea/Bind9.
Changes:
- Fixed prerequisite record placement in the
update()function by changingadd_update()toadd_pre_requisite() - Added comprehensive unit test to validate the fix and prevent future regressions
- Reorganized test module imports to support the new test
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| //let from_kea = BASE64_STANDARD.decode("wbYoAAABAAEAAgABA2xhYgAABgABCG91dHJpZGVywAwA/wD+AAAAAAAAwBUAAQABAAAEsAAECkVFNMAVADEAAQAABLAAIwABAasTowCr6hY874Nyfq0krHOxnvk5GwMgYIi6N1UY6lTRCGtlYS1iaW5kAAD6AP8AAAAAAD0LaG1hYy1zaGEyNTYAAABpbnyOASwAIB0XNv7B7IFpFMfsWXNH4jrSjqApS61geEUuVlin/bPBMsUAAAAA").unwrap(); | ||
| //let message = Message::from_vec(from_kea.as_slice()); | ||
| //println!("{:#?}", message); |
Copilot
AI
Jan 25, 2026
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.
Consider removing the commented-out code. If this reference code is valuable for future debugging or documentation, it should either be moved to a comment explaining the test's origin, or removed entirely to keep the codebase clean.
| //let from_kea = BASE64_STANDARD.decode("wbYoAAABAAEAAgABA2xhYgAABgABCG91dHJpZGVywAwA/wD+AAAAAAAAwBUAAQABAAAEsAAECkVFNMAVADEAAQAABLAAIwABAasTowCr6hY874Nyfq0krHOxnvk5GwMgYIi6N1UY6lTRCGtlYS1iaW5kAAD6AP8AAAAAAD0LaG1hYy1zaGEyNTYAAABpbnyOASwAIB0XNv7B7IFpFMfsWXNH4jrSjqApS61geEUuVlin/bPBMsUAAAAA").unwrap(); | |
| //let message = Message::from_vec(from_kea.as_slice()); | |
| //println!("{:#?}", message); |
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.
Is there value in keeping this and doing an assert or just deleting the comment?
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.
I think it's a useful reference but that is loosely held. No issue to delete it.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Neat. I'll fix the unnecessary clone but I'm not sure what to do about the commenting out of the packet I used for testing. Open to any better suggestion than leaving it. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
I got copilot to fix the clone, had some issues with cargo fmt but that's resolved. I'm going to merge as-is. If we can use the kea output in a test Thanks again for your effort |
|
Thank you! I'll come up with a plan for adding some options to the ddns config, does creating an issue here work? |
|
sure, create an issue with a proposal and I'll have a look. Note: I reverted the update_present as well based on my reading of the RFC:
|
Patch for a regression introduced in the previous ddns commit.
To be more thorough in the testing I setup a lab with latest Kea/Bind for a Debian 13. I then exercised the setup to verify it worked as expected, DDNS updates were added/delete/updated as expected. From there I did a packet capture and compared with what Dora was sending. This commit has the fix for the regression and a unit test to validate. I replayed the Kea/Bind9 workflows with Dora acting as the DHCP server and everything worked as expected.