Expected answer section to have 2 entries got 1 on last stage

#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string>
#include <cerrno>
#include <arpa/inet.h>
#include<vector>



std::string forward_address; 
std::string parseName(const char* buffer, int& offset, int maxLen) {
    std::string name;
    while (offset < maxLen) {
        uint8_t len = buffer[offset++];
        if (len == 0) {
            name .push_back(0);
            break;
        }
        if ((len & 0xC0) == 0xC0) {
            // pointer
            uint16_t ptr;
            memcpy(&ptr, buffer + offset - 1, 2);
            ptr = ntohs(ptr) & 0x3FFF;
            int dummy = ptr;
            return parseName(buffer, dummy, maxLen);
        } else {
            name += len;
            for (int i = 0; i < len; ++i) {
                name += buffer[offset++];
            }
        }
    }
    return name;
}

class header{
    private:
    std::string res;
    public:
    header(){
        res.resize(12,0);
    }
    void set_pid(uint16_t pid){
       res[0] = (pid >> 8) & 0xFF;
res[1] = pid & 0xFF;

    }
    void set_flags(uint16_t flags){
       res[2] = (flags >> 8) & 0xFF;
res[3] = flags & 0xFF;

    }
    void set_qdcount(uint16_t qcnt){
       res[4] = (qcnt >> 8) & 0xFF;
res[5] = qcnt & 0xFF;

    }
    void set_ancount(uint16_t acnt){
        res[6] = (acnt >> 8) & 0xFF;
res[7] = acnt & 0xFF;

    }
    void set_nscount(uint16_t auth_cnt){
       res[8] = (auth_cnt >> 8) & 0xFF;
res[9] = auth_cnt & 0xFF;

    }
    void set_arcount(uint16_t addt_cnt){
       res[10] = (addt_cnt >> 8) & 0xFF;
res[11] = addt_cnt & 0xFF;

    }
    std::string get_packet(){
        return res;
    }
};
int main(int argc ,char *argv[]) {
    // Flush after every std::cout / std::cerr
    std::cout << std::unitbuf;
    std::cerr << std::unitbuf;

    if(argc>=3) forward_address=argv[2];

    // Disable output buffering
    setbuf(stdout, NULL);

    // You can use print statements as follows for debugging, they'll be visible when running tests.
    std::cout << "Logs from your program will appear here!" << std::endl;

      // Uncomment this block to pass the first stage
   int udpSocket;
   struct sockaddr_in clientAddress;
//
   udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
   if (udpSocket == -1) {
       std::cerr << "Socket creation failed: " << strerror(errno) << "..." << std::endl;
       return 1;
   }

   // Since the tester restarts your program quite often, setting REUSE_PORT
   // ensures that we don't run into 'Address already in use' errors
   int reuse = 1;
   if (setsockopt(udpSocket, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)) < 0) {
       std::cerr << "SO_REUSEPORT failed: " << strerror(errno) << std::endl;
       return 1;
   }

   sockaddr_in serv_addr = { .sin_family = AF_INET,
                             .sin_port = htons(2053),
                             .sin_addr = { htonl(INADDR_ANY) },
                           };

   if (bind(udpSocket, reinterpret_cast<struct sockaddr*>(&serv_addr), sizeof(serv_addr)) != 0) {
       std::cerr << "Bind failed: " << strerror(errno) << std::endl;
       return 1;
   }

   int bytesRead;
   char buffer[512];
   socklen_t clientAddrLen = sizeof(clientAddress);

   while (true) {
       // Receive data
       bytesRead = recvfrom(udpSocket, buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr*>(&clientAddress), &clientAddrLen);
       if (bytesRead == -1) {
           perror("Error receiving data");
           break;
       }

       buffer[bytesRead] = '\0';
       std::cout << "Received " << bytesRead << " bytes: " << buffer << std::endl;
       //parsing header
       uint16_t flags,flags2;
       uint16_t pid;
       uint16_t qcnt;
       memcpy(&pid,buffer,sizeof(pid));
       pid=ntohs(pid);
       memcpy(&flags,buffer+2,sizeof(flags));
       memcpy(&flags2,buffer+2,sizeof(flags2));
       flags|=htons(static_cast<uint16_t>(1 << 15));
       flags=ntohs(flags);
       flags2=ntohs(flags2);
       memcpy(&qcnt,buffer+4,sizeof(qcnt));
       qcnt = ntohs(qcnt);
       uint8_t opcode = (flags >> 11) & 0x0F;
       if(opcode!=0){
        flags=(flags|0x04);
       }
       flags=(flags|0x8000);


       // Create an empty response
       header* res_packet=new header;

       res_packet->set_pid(pid);
       res_packet->set_flags(flags);

       header* res_packet2=new header;

       res_packet2->set_pid(pid);
       res_packet2->set_flags(flags2);
       res_packet2->set_qdcount(1);
       std::string head2=res_packet2->get_packet();

       //question
      std::vector<std::string>questions,answers;
       int offset = 12;  // DNS header is 12 bytes




      while(qcnt){
        std::string qs,ans;
       
uint32_t ttl = htonl(60);  // ✅ Use htonl not htons for TTL (it's 4 bytes)
uint16_t d_length = htons(4);
uint8_t data[] = {8, 8, 8, 8};  // IP address

auto buildAnswer = [&](const std::string &name) {
    std::string a = name;
    uint16_t type = htons(1);   // Type A
    uint16_t clas = htons(1);   // IN
    a.append(reinterpret_cast<const char*>(&type), 2);
    a.append(reinterpret_cast<const char*>(&clas), 2);
    a.append(reinterpret_cast<const char*>(&ttl), 4);
    a.append(reinterpret_cast<const char*>(&d_length), 2);
    a.append(reinterpret_cast<const char*>(data), 4);
    return a;
};



std::string name = parseName(buffer, offset, bytesRead);
qs = name;
qs.append(buffer + offset, 4); // QTYPE + QCLASS
ans=buildAnswer(name);
offset += 4;

questions.push_back(qs);
answers.push_back(ans);
qcnt--;
      }

         std::cout<<(int)questions.size()<<std::endl;



    

// Final response
std::string res;
std::vector<std::string>answers2;
uint16_t cnt_a=0;
if (!forward_address.empty()) {
    
    int ind = forward_address.find(':');
    std::string ip = forward_address.substr(0, ind);
    int port = std::stoi(forward_address.substr(ind + 1));

    int c_s = socket(AF_INET, SOCK_DGRAM, 0);
    sockaddr_in server_add{};
    server_add.sin_family = AF_INET;
    server_add.sin_port = htons(port);
    inet_pton(AF_INET, ip.c_str(), &server_add.sin_addr);

    for (const std::string& question : questions) {
        
        std::string req2 = head2 + question;
        sendto(c_s, req2.data(), req2.size(), 0, reinterpret_cast<sockaddr*>(&server_add), sizeof(server_add));

        char buf2[512];
        sockaddr_in recv_addr{};
        socklen_t addr_len = sizeof(recv_addr);
        int bytes_received = recvfrom(c_s, buf2, sizeof(buf2), 0, reinterpret_cast<sockaddr*>(&recv_addr), &addr_len);

       int ans_offset = 12;
       uint16_t an_cnt;
       memcpy(&an_cnt,buf2+6,2);
       an_cnt=ntohs(an_cnt);
       cnt_a+=an_cnt;
// Skip question section
parseName(buf2, ans_offset, bytes_received);
ans_offset += 4;  // QTYPE + QCLASS

std::string an(buf2 + ans_offset, bytes_received - ans_offset);
answers2.push_back(an);

    }
    close(c_s);

    res_packet->set_qdcount(questions.size());
    res_packet->set_ancount(cnt_a);
    std::string head = res_packet->get_packet();
    res = head ;
for(int i=0;i<questions.size();i++){
    res+=questions[i];
}
    for (const std::string& ans : answers2) {
        res += ans;
    }
    

}

else{
    //std::cout<<(int)answers.size()<<std::endl;
res_packet->set_qdcount(questions.size());
    res_packet->set_ancount(answers.size());
    std::string head = res_packet->get_packet();
    res = head ;
for(int i=0;i<questions.size();i++){
    res+=questions[i];
}
    for (const std::string& ans : answers) {
        res += ans;
    }
    
}



       // Send response
       if (sendto(udpSocket, res.data(), res.size(), 0, reinterpret_cast<struct sockaddr*>(&clientAddress), sizeof(clientAddress)) == -1) {
           perror("Failed to send response");
       }
   }

   close(udpSocket);

    return 0;
}

please someone help

Hey @ronak1293, could you upload your code to GitHub and share the link? It will be much easier to debug if I can run it directly.

sure , this is the link => GitHub - ronak1293/dns-server

@ronak1293 Looks like you’ve got past this stage. :tada:

Would you mind sharing what was wrong? Love to see if we can improve the tester / instructions.

In last stage forwarding server somtimes not sending any ans section (ancnt==0) ,so i have to add that condition.

1 Like

Y’all might consider adjusting the earlier challenges such that we are expected to resolve specific domains, and provide empty answers to unknown domains. That way, we’re not going in to the last challenge expecting non-empty replies.

1 Like