refactoring + rcode handling, setup for opcode handling

This commit is contained in:
LovroG05
2025-03-17 21:14:09 +01:00
parent 48be00e686
commit e9c6582b5d
3 changed files with 87 additions and 58 deletions

View File

@@ -11,13 +11,13 @@ For now, it successfully recursively resolves A records.
- [x] Can parse and respond to valid DNS queries
- [ ] Correctly handles headers (ID, flags, opcode, RCODE)
- [ ] Supports question section processing
- [ ] Correctly formats answers, authority, and additional sections
- [x] Correctly formats answers, authority, and additional sections
### Supported Query Types
- [x] A (IPv4 address)
- [ ] AAAA (IPv6 address)
- [ ] CNAME (Canonical name)
- [x] CNAME (Canonical name)
- [ ] NS (Nameserver)
- [ ] MX (Mail Exchange)
- [ ] TXT (Text records)
@@ -26,7 +26,7 @@ For now, it successfully recursively resolves A records.
### Query Processing
- [ ] Supports recursive queries (if implemented)
- [x] Supports recursive queries (if implemented)
- [ ] Supports iterative queries (if acting as authoritative server)
- [ ] Correctly handles RD (Recursion Desired) flag
- [ ] Supports negative responses (NXDOMAIN, NODATA)
@@ -39,8 +39,8 @@ For now, it successfully recursively resolves A records.
### Error Handling
- [ ] Responds correctly to malformed queries
- [ ] Returns correct RCODE values (e.g., SERVFAIL, REFUSED, NXDOMAIN)
- [x] Responds correctly to malformed queries
- [x] Returns correct RCODE values (e.g., SERVFAIL, REFUSED, NXDOMAIN) // some
- [ ] Handles timeouts and retransmissions
## Testing

View File

@@ -81,63 +81,40 @@ func (s *Server) Close() {
// Process a single DNS query request.
func (s *Server) process(addr net.Addr, buf []byte) {
defer s.wg.Done()
rcode := dnsmessage.RCodeSuccess
// Parse incoming DNS query
msg, err := resolver.PacketParser(buf)
if err != nil {
log.Printf("Packet parsing error from %s: %v", addr, err)
return
rcode = dnsmessage.RCodeFormatError
}
opcode := msg.Header.OpCode
rd := msg.Header.RecursionDesired
var records []entities.Record
for _, q := range msg.Questions {
if q.Type == dnsmessage.TypeA {
nips, err := resolver.ResolveDN(q.Name, msg.Header.ID, dnsmessage.TypeA, s.cache)
if err != nil {
log.Printf("Resolution error from %s for %s: %v", addr, q.Name, err)
continue
}
if rcode == dnsmessage.RCodeSuccess {
for _, q := range msg.Questions {
if q.Type == dnsmessage.TypeA {
nips, err := resolver.ResolveDN(q.Name, msg.Header.ID, dnsmessage.TypeA, s.cache)
if err != nil {
log.Printf("Resolution error from %s for %s: %v", addr, q.Name, err)
if len(nips) == 0 {
log.Printf("No IP found for %s from %s", q.Name.String(), addr)
continue
}
continue
}
records = append(records, nips...)
if len(nips) == 0 {
log.Printf("No IP found for %s from %s", q.Name.String(), addr)
continue
}
records = append(records, nips...)
}
}
}
// Build DNS answer section
var answers []dnsmessage.Resource
for _, record := range records {
answers = append(answers, dnsmessage.Resource{
Header: dnsmessage.ResourceHeader{
Name: record.Name,
Type: record.RType,
Class: record.Class,
TTL: record.TTL,
},
Body: &dnsmessage.AResource{
A: [4]byte{record.IP[0], record.IP[1], record.IP[2], record.IP[3]},
},
})
}
// Prepare the response message
response := dnsmessage.Message{
Header: dnsmessage.Header{
ID: msg.Header.ID,
Response: true,
OpCode: msg.Header.OpCode,
Authoritative: false,
RecursionDesired: msg.Header.RecursionDesired,
RecursionAvailable: true,
RCode: dnsmessage.RCodeSuccess,
},
Questions: msg.Questions,
Answers: answers,
}
response := s.buildReplyMessage(msg.Header.ID, opcode, rd, rcode, msg.Questions, records)
// Pack the response
packed, err := response.Pack()
if err != nil {
@@ -146,8 +123,50 @@ func (s *Server) process(addr net.Addr, buf []byte) {
}
// Send the response back to the client
_, err = s.udpServer.WriteTo(packed, addr)
err = s.reply(addr, packed)
if err != nil {
log.Printf("Response write error to %s: %v", addr, err)
log.Printf("Error replying to %s: %v", addr, err)
}
}
func (s *Server) reply(addr net.Addr, buf []byte) error {
_, err := s.udpServer.WriteTo(buf, addr)
return err
}
func (s *Server) buildReplyMessage(id uint16, opcode dnsmessage.OpCode, rd bool, rcode dnsmessage.RCode, questions []dnsmessage.Question, records []entities.Record) dnsmessage.Message {
var answers []dnsmessage.Resource
if records != nil {
// Build DNS answer section
for _, record := range records {
answers = append(answers, dnsmessage.Resource{
Header: dnsmessage.ResourceHeader{
Name: record.Name,
Type: record.RType,
Class: record.Class,
TTL: record.TTL,
},
Body: &dnsmessage.AResource{
A: [4]byte{record.IP[0], record.IP[1], record.IP[2], record.IP[3]},
},
})
}
}
response := dnsmessage.Message{
Header: dnsmessage.Header{
ID: id,
Response: true,
OpCode: opcode,
Authoritative: false, // this server is not supported as an authoritative server
RecursionDesired: rd,
RecursionAvailable: true, // If it supports recursion (RA flag is set), it will perform the necessary queries to resolve www.example.com and return the final IP address to the client - NO OTHER MODE CURRENTLY SUPPORTED
RCode: rcode,
},
Questions: questions,
Answers: answers,
}
return response
}

View File

@@ -16,8 +16,8 @@ func Test_CorrectlyHandlesHeaders(t *testing.T) {
}
// Check that the query ID is the same as the request ID
if response.Header.ID != 12345 { // Replace with the ID used in your request (or dynamic query ID)
t.Errorf("Expected query ID to be 12345, but got %d", response.Header.ID)
if response.Header.ID != 1234 { // Replace with the ID used in your request (or dynamic query ID)
t.Errorf("Expected query ID to be 1234, but got %d", response.Header.ID)
}
// Check that the query response (qr) flag is set to 1, meaning it's a response
@@ -30,10 +30,10 @@ func Test_CorrectlyHandlesHeaders(t *testing.T) {
t.Errorf("Expected rd (recursion desired) flag to be set to 1, but it was 0")
}
// Check that recursion available (ra) flag is set to 1 in the response (assuming recursion is available)
/*// Check that recursion available (ra) flag is set to 1 in the response (assuming recursion is available)
if response.Header.RecursionAvailable != true {
t.Errorf("Expected ra (recursion available) flag to be set to 1, but it was 0")
}
}*/
// Check that the opcode is set to 0 (standard query)
if response.Header.OpCode != 0 {
@@ -51,6 +51,16 @@ func Test_CorrectlyHandlesHeaders(t *testing.T) {
}
}
func Test_RecursionAvailable(t *testing.T) {
response := sendDNSQuery(t, "127.0.0.1:53", "govekar.net.", dnsmessage.TypeA, true)
if response.Header.RCode != dnsmessage.RCodeSuccess {
t.Errorf("Expected RCODE 0 (NoError), got %d", response.Header.RCode)
}
if response.Header.RecursionAvailable != true {
t.Errorf("Expected recursion available to 1, but got 0")
}
}
func Test_CorrectlyFormatsSections(t *testing.T) {
// Send a DNS query for a known domain (e.g., "govekar.net")
response := sendDNSQuery(t, "127.0.0.1:53", "govekar.net.", dnsmessage.TypeA, true)
@@ -69,7 +79,7 @@ func Test_CorrectlyFormatsSections(t *testing.T) {
}
// Test the authority section, which should contain authoritative name server records if the server is authoritative
if len(response.Authorities) > 0 {
if len(response.Authorities) > 0 && response.Header.Authoritative {
// Ensure that the authority section contains NS (Name Server) records
for _, auth := range response.Authorities {
if auth.Header.Type != dnsmessage.TypeNS {
@@ -77,8 +87,8 @@ func Test_CorrectlyFormatsSections(t *testing.T) {
}
}
} else {
t.Log("No authority section found (expected if server is not authoritative)")
} else if response.Header.Authoritative {
t.Log("No authority section found (expected if server is authoritative)")
}
// Test the additional section, which should contain A records for the name servers listed in the authority section