c-ares
DNS server
networking
C programming
DNS lookup

how to get DNS server in c-ares

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

If you want to know which DNS servers a c-ares channel is using, the answer is not buried inside a resolver query callback. c-ares exposes dedicated APIs for reading the configured server list after the channel is initialized. The main choice is whether you use the newer string-based API or the older linked-list style functions.

Initialize the Channel First

The server list belongs to an ares_channel, so you need to initialize c-ares and create a channel before you ask for it.

c
1#include <ares.h>
2#include <stdio.h>
3
4int main(void) {
5    ares_channel_t *channel;
6
7    if (ares_library_init(ARES_LIB_INIT_ALL) != ARES_SUCCESS) {
8        return 1;
9    }
10
11    if (ares_init(&channel) != ARES_SUCCESS) {
12        ares_library_cleanup();
13        return 1;
14    }
15
16    /* read configured DNS servers here */
17
18    ares_destroy(channel);
19    ares_library_cleanup();
20    return 0;
21}

After ares_init, c-ares has loaded its resolver configuration from the operating system or from options you provided explicitly.

Preferred Modern Option: ares_get_servers_csv

In newer c-ares versions, the easiest way to inspect the configured servers is ares_get_servers_csv. It returns the server list as a CSV string that you can print or parse.

c
1#include <ares.h>
2#include <stdio.h>
3
4int main(void) {
5    ares_channel_t *channel;
6    char *csv;
7
8    if (ares_library_init(ARES_LIB_INIT_ALL) != ARES_SUCCESS) {
9        return 1;
10    }
11
12    if (ares_init(&channel) != ARES_SUCCESS) {
13        ares_library_cleanup();
14        return 1;
15    }
16
17    csv = ares_get_servers_csv(channel);
18    if (csv != NULL) {
19        printf("DNS servers: %s\n", csv);
20        ares_free_string(csv);
21    }
22
23    ares_destroy(channel);
24    ares_library_cleanup();
25    return 0;
26}

This is often the simplest answer if your installed c-ares version supports it. In current c-ares documentation, the older linked-list retrieval functions are marked deprecated in favor of this CSV-based API.

Older API: ares_get_servers

Older code often uses ares_get_servers or ares_get_servers_ports. Those APIs return a linked list of ares_addr_node or ares_addr_port_node structures. That works fine, but it is more verbose:

c
1#include <ares.h>
2#include <arpa/inet.h>
3#include <stdio.h>
4
5int main(void) {
6    ares_channel_t *channel;
7    struct ares_addr_node *servers = NULL;
8
9    if (ares_library_init(ARES_LIB_INIT_ALL) != ARES_SUCCESS) {
10        return 1;
11    }
12
13    if (ares_init(&channel) != ARES_SUCCESS) {
14        ares_library_cleanup();
15        return 1;
16    }
17
18    if (ares_get_servers(channel, &servers) == ARES_SUCCESS) {
19        for (struct ares_addr_node *node = servers; node != NULL; node = node->next) {
20            char buffer[INET6_ADDRSTRLEN];
21            const void *addr =
22                (node->family == AF_INET) ? (const void *)&node->addr.addr4
23                                          : (const void *)&node->addr.addr6;
24
25            if (inet_ntop(node->family, addr, buffer, sizeof(buffer)) != NULL) {
26                printf("DNS server: %s\n", buffer);
27            }
28        }
29
30        ares_free_data(servers);
31    }
32
33    ares_destroy(channel);
34    ares_library_cleanup();
35    return 0;
36}

This older form is still useful when you want structured access to each address rather than a single string.

Why the Channel Matters

c-ares does not expose one global resolver state that every program shares uniformly. Different channels can be initialized with different options, including custom server lists. So the correct question is not only "what DNS servers does the machine use" but also "what DNS servers does this channel use after initialization and overrides."

That distinction matters if you call ares_set_servers, pass options during initialization, or load configuration from a non-default environment.

Common Pitfalls

  • Trying to read DNS server information before calling ares_init.
  • Assuming the process has one global resolver list when different c-ares channels may be configured differently.
  • Using ares_get_servers without freeing the returned data with ares_free_data.
  • Forgetting that the newer CSV API and older linked-list APIs may differ by c-ares version.
  • Confusing the configured server list with the result of a specific DNS query.

Summary

  • c-ares exposes APIs to read the configured DNS server list from an initialized channel.
  • 'ares_get_servers_csv is the simplest modern option when available.'
  • 'ares_get_servers and related older APIs return structured linked-list data.'
  • Always free returned memory with the matching c-ares cleanup function.
  • Think in terms of channel configuration, not just machine-wide resolver settings.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.