Extract DNS record (MX) details from name server reply c++

1.7k views Asked by At

I am trying to get the hostname of the MX record with low priority using the following code, but I am unable to parse the required details (hostname, ttl, priority) from the given nameserver reply.

u_char nsbuf[4096], dispbuf[4096];
ns_msg msg;
ns_rr rr;
int i, j, l;
std::string domain("gmail.com");
l = res_query(domain.c_str(), ns_c_any, ns_t_mx, nsbuf, sizeof (nsbuf));

    ns_initparse(nsbuf, l, &msg);
    printf("%s :\n", domain.c_str());
    l = ns_msg_count(msg, ns_s_an);
    for (j = 0; j < l; j++)
    {
        int prr = ns_parserr(&msg, ns_s_an, j, &rr);


        ns_sprintrr(&msg, &rr, NULL, NULL, reinterpret_cast<char*> (dispbuf), sizeof (dispbuf));

        printf("%s\n", dispbuf);
    }

The above code gives the result as

gmail.com. 15M IN MX 30 alt3.gmail-smtp-in.l.google.com.

Is there any available function to get hostname, priority, ttl, etc. in separate buffers like below?

host -> alt3.gmail-smtp-in.l.google.com

priority -> 30

ttl -> 15M

And, should we manually check for the higher priority record, or is there any utility function or code that could do the requirement?

Edit:

I tried the following code to extract data

#include <cstdlib>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <resolv.h>
#include <cstring>
#include <string>
#include <string.h>

using namespace std;
int main(int argc, char** argv) {

    u_char nsbuf[4096];
    u_char dispbuf[4096];
    ns_msg msg;
    ns_rr rr;
    int i, j, l;
    std::string domain("gmail.com");
    l = res_query(domain.c_str(), ns_c_any, ns_t_mx, nsbuf, sizeof (nsbuf));
    if (l < 0) {
        perror(domain.c_str());
    } else {
#ifdef USE_PQUERY
        res_pquery(&_res, nsbuf, l, stdout);
#else
        ns_initparse(nsbuf, l, &msg);
        l = ns_msg_count(msg, ns_s_an);
        for (j = 0; j < l; j++) {
            int prr = ns_parserr(&msg, ns_s_an, j, &rr);

            //BLOCK 1
            char *cp;
            cp = (char *) ns_rr_name(rr);
            printf("CP->%s\n", (char *) cp);
            int i1 = ns_rr_type(rr);
            printf("Type->%d\n", i1);
            int i2 = ns_rr_class(rr);
            printf("Class->%d\n", i2);
            int i3 = ns_rr_ttl(rr);
            printf("TTL->%d\n", i3);
            int i4 = ns_rr_rdlen(rr);
            printf("DataLength->%d\n", i4);

            //BLOCK 2
            const u_char *rdata = ns_rr_rdata(rr) +1;
            printf("DataU_char-> %s\n", reinterpret_cast<const char*> (rdata));

            int len = strlen(reinterpret_cast<const char*> (rdata));
            printf("len->%d\n", len);

            char rdatatemp[1024];
            strncpy(rdatatemp, reinterpret_cast<const char*> (rdata), sizeof (rdatatemp));
            printf("DataChar->%s\n", rdatatemp);

            ns_sprintrr(&msg, &rr, NULL, NULL, reinterpret_cast<char*> (dispbuf), sizeof (dispbuf));
            printf("FullRecord->%s\n", dispbuf);
            printf("\n");
        }
#endif
    }
    return 0;
}

The above code works well for txt record, but for mx record, it is not parsed correctly and the following is the result

Output:

CP->gmail.com
Type->15
Class->1
TTL->130
DataLength->32
DataU_char-> gmail-smtp-inlgoogle��
len->33
DataChar->gmail-smtp-inlgoogle��
FullRecord->gmail.com. 2m10s IN MX 30 alt3.gmail-smtp-in.l.google.com.

CP->gmail.com
Type->15
Class->1
TTL->130
DataLength->9
DataU_char-> alt2�.�
len->10
DataChar->alt2�.�
FullRecord->gmail.com. 2m10s IN MX 20 alt2.gmail-smtp-in.l.google.com.

So in DataChar & DataU_char special symbols are printed.
'alt2�.�' is printed instead of 'alt2.gmail-smtp-in.l.google.com.'
Also the DataLength value is wrong.
Also I am not able to get the priority of the record.
Am I missing something here, or is it the error with the c++ library itself?

1

There are 1 answers

4
Alnitak On BEST ANSWER

libresolv doesn't have public functions to unpack specific resource record types, but there are functions inside it to help you do it yourself.

In particular, look at dn_expand which can read a (compressed) domain name and ns_get16 which will read a big-endian two-octet field from a record, so in your case:

char exchange[NS_MAXDNAME];

const u_char *rdata = ns_rr_rdata(rr);

const uint16_t pri = ns_get16(rdata);
int len = dn_expand(nsbuf, nsbuf + msg_len, rdata + 2, exchange, sizeof(exchange));

printf("Pri->%d\n", pri);
printf("Exchange->%s\n", exchange);

where msg_len replaces your overwritten l variable, containing the length of the received packet.

The rdata + 2 in the call to dn_expand() skips the 16 bit priority field.