Skip to content

Instantly share code, notes, and snippets.

@wil
Created August 2, 2013 16:27
Show Gist options
  • Save wil/6141275 to your computer and use it in GitHub Desktop.
Save wil/6141275 to your computer and use it in GitHub Desktop.
Demonstrates how to send a DNS query and parse response in C using libresolv.
/*
* Copyright (c) 2013 by Wil Tan <[email protected]>
*
* Based on dump_dns.c from the dnscap <https://www.dns-oarc.net/tools/dnscap>
* originally written by Paul Vixie.
*
* Copyright (c) 2007 by Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
extern const char *_res_opcodes[];
#define MY_GET16(s, cp) do { \
register const u_char *t_cp = (const u_char *)(cp); \
(s) = ((u_int16_t)t_cp[0] << 8) \
| ((u_int16_t)t_cp[1]) \
; \
(cp) += NS_INT16SZ; \
} while (0)
#define MY_GET32(l, cp) do { \
register const u_char *t_cp = (const u_char *)(cp); \
(l) = ((u_int32_t)t_cp[0] << 24) \
| ((u_int32_t)t_cp[1] << 16) \
| ((u_int32_t)t_cp[2] << 8) \
| ((u_int32_t)t_cp[3]) \
; \
(cp) += NS_INT32SZ; \
} while (0)
static void
dump_dns_rr(ns_msg *msg, ns_rr *rr, ns_sect sect, FILE *trace) {
char buf[NS_MAXDNAME];
u_int class, type;
const u_char *rd;
u_int32_t soa[5];
u_int16_t mx;
int n;
class = ns_rr_class(*rr);
type = ns_rr_type(*rr);
fprintf(trace, "%s,%s,%s",
ns_rr_name(*rr),
p_class(class),
p_type(type));
if (sect == ns_s_qd)
return;
fprintf(trace, ",%lu", (u_long)ns_rr_ttl(*rr));
rd = ns_rr_rdata(*rr);
switch (type) {
case ns_t_soa:
n = ns_name_uncompress(ns_msg_base(*msg), ns_msg_end(*msg),
rd, buf, sizeof buf);
if (n < 0)
goto error;
putc(',', trace);
fputs(buf, trace);
rd += n;
n = ns_name_uncompress(ns_msg_base(*msg), ns_msg_end(*msg),
rd, buf, sizeof buf);
if (n < 0)
goto error;
putc(',', trace);
fputs(buf, trace);
rd += n;
if (ns_msg_end(*msg) - rd < 5*NS_INT32SZ)
goto error;
for (n = 0; n < 5; n++)
MY_GET32(soa[n], rd);
sprintf(buf, "%u,%u,%u,%u,%u",
soa[0], soa[1], soa[2], soa[3], soa[4]);
break;
case ns_t_a:
inet_ntop(AF_INET, rd, buf, sizeof buf);
break;
case ns_t_aaaa:
inet_ntop(AF_INET6, rd, buf, sizeof buf);
break;
case ns_t_mx:
MY_GET16(mx, rd);
fprintf(trace, ",%u", mx);
/* FALLTHROUGH */
case ns_t_ns:
case ns_t_ptr:
case ns_t_cname:
n = ns_name_uncompress(ns_msg_base(*msg), ns_msg_end(*msg),
rd, buf, sizeof buf);
if (n < 0)
goto error;
break;
case ns_t_txt:
snprintf(buf, (size_t)rd[0]+1, "%s", rd+1);
break;
default:
error:
sprintf(buf, "[%u]", ns_rr_rdlen(*rr));
}
if (buf[0] != '\0') {
putc(',', trace);
fputs(buf, trace);
}
}
static void
dump_dns_sect(ns_msg *msg, ns_sect sect, FILE *trace, const char *endline) {
int rrnum, rrmax;
const char *sep;
ns_rr rr;
rrmax = ns_msg_count(*msg, sect);
if (rrmax == 0) {
fputs(" 0", trace);
return;
}
fprintf(trace, " %s%d", endline, rrmax);
sep = "";
for (rrnum = 0; rrnum < rrmax; rrnum++) {
if (ns_parserr(msg, sect, rrnum, &rr)) {
fputs(strerror(errno), trace);
return;
}
fprintf(trace, " %s", sep);
dump_dns_rr(msg, &rr, sect, trace);
sep = endline;
}
}
void
dump_dns(const u_char *payload, size_t paylen,
FILE *trace, const char *endline)
{
u_int opcode, rcode, id;
const char *sep;
ns_msg msg;
fprintf(trace, " %sdns ", endline);
if (ns_initparse(payload, paylen, &msg) < 0) {
fputs(strerror(errno), trace);
return;
}
opcode = ns_msg_getflag(msg, ns_f_opcode);
rcode = ns_msg_getflag(msg, ns_f_rcode);
id = ns_msg_id(msg);
fprintf(trace, "%s,%s,%u", _res_opcodes[opcode], p_rcode(rcode), id);
sep = ",";
#define FLAG(t,f) if (ns_msg_getflag(msg, f)) { \
fprintf(trace, "%s%s", sep, t); \
sep = "|"; \
}
FLAG("qr", ns_f_qr);
FLAG("aa", ns_f_aa);
FLAG("tc", ns_f_tc);
FLAG("rd", ns_f_rd);
FLAG("ra", ns_f_ra);
FLAG("z", ns_f_z);
FLAG("ad", ns_f_ad);
FLAG("cd", ns_f_cd);
#undef FLAG
dump_dns_sect(&msg, ns_s_an, trace, endline);
}
int main() {
u_char answer[1024] = "";
res_init();
int rv = res_query("google.com", ns_c_in, ns_t_txt, answer, sizeof(answer));
printf("rv=%d\n", rv);
dump_dns(answer, rv, stdout, "\n");
printf("\n");
}
@vineyyadav
Copy link

Is it possible to specify the DNS server to be used for the query?

@xjuric29
Copy link

Is it possible to specify the DNS server to be used for the query?

Yes. All you need is to modify the global variable "_res" initialized by "res_init" function. In this structure is an array of used nameservers called "nsaddr_list" with IP addresses of nameservers from the "/etc/resolv.conf" file. You can easily replace them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment