refactoring + rcode handling, setup for opcode handling
This commit is contained in:
10
README.md
10
README.md
@@ -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
|
||||
|
||||
111
server/Server.go
111
server/Server.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user