fury.slay
fury.slay

Reputation: 1258

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

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?

Upvotes: 1

Views: 1842

Answers (1)

Alnitak
Alnitak

Reputation: 339816

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.

Upvotes: 1

Related Questions