c-ares provides a set of library functions, datatypes, and enumerations which integrators will use for their implementations. When you install c-ares, you get man pages which describe their use and meanings.

Manpages

example_ev.c:

#include <stdio.h>
#include <string.h>
#include <ares.h>

/* Callback that is called when DNS query is finished */
static void addrinfo_cb(void *arg, int status, int timeouts,
                        struct ares_addrinfo *result)
{
  (void)arg; /* Example does not use user context */
  printf("Result: %s, timeouts: %d\n", ares_strerror(status), timeouts);

  if (result) {
    struct ares_addrinfo_node *node;
    for (node = result->nodes; node != NULL; node = node->ai_next) {
      char        addr_buf[64] = "";
      const void *ptr          = NULL;
      if (node->ai_family == AF_INET) {
        const struct sockaddr_in *in_addr =
          (const struct sockaddr_in *)((void *)node->ai_addr);
        ptr = &in_addr->sin_addr;
      } else if (node->ai_family == AF_INET6) {
        const struct sockaddr_in6 *in_addr =
          (const struct sockaddr_in6 *)((void *)node->ai_addr);
        ptr = &in_addr->sin6_addr;
      } else {
        continue;
      }
      ares_inet_ntop(node->ai_family, ptr, addr_buf, sizeof(addr_buf));
      printf("Addr: %s\n", addr_buf);
    }
  }
  ares_freeaddrinfo(result);
}

int main(int argc, char **argv)
{
  ares_channel_t            *channel = NULL;
  struct ares_options        options;
  int                        optmask = 0;
  struct ares_addrinfo_hints hints;

  if (argc != 2) {
    printf("Usage: %s domain\n", argv[0]);
    return 1;
  }

  /* Initialize library */
  ares_library_init(ARES_LIB_INIT_ALL);

  if (!ares_threadsafety()) {
    printf("c-ares not compiled with thread support\n");
    return 1;
  }

  /* Enable event thread so we don't have to monitor file descriptors */
  memset(&options, 0, sizeof(options));
  optmask      |= ARES_OPT_EVENT_THREAD;
  options.evsys = ARES_EVSYS_DEFAULT;

  /* Initialize channel to run queries, a single channel can accept unlimited
   * queries */
  if (ares_init_options(&channel, &options, optmask) != ARES_SUCCESS) {
    printf("c-ares initialization issue\n");
    return 1;
  }

  /* Perform an IPv4 and IPv6 request for the provided domain name */
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_flags  = ARES_AI_CANONNAME;
  ares_getaddrinfo(channel, argv[1], NULL, &hints, addrinfo_cb,
                   NULL /* user context not specified */);

  /* Wait until no more requests are left to be processed */
  ares_queue_wait_empty(channel, -1);

  /* Cleanup */
  ares_destroy(channel);

  ares_library_cleanup();
  return 0;
}

Compilation:

cc -I/usr/local/include -o example_ev example_ev.c -Wl,-rpath /usr/local/lib -lcares

Sock State Callback example

example_ss.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h>
#include <ares.h>

typedef struct {
  ares_channel_t *channel;
  struct pollfd  *fds;
  size_t          nfds;
} dnsstate_t;

void sock_state_cb(void *data, ares_socket_t socket_fd, int readable, int writable)
{
  dnsstate_t *state = data;
  size_t      idx;

  /* Find match */
  for (idx=0; idx<state->nfds; idx++) {
    if (state->fds[idx].fd == socket_fd) {
      break;
    }
  }

  /* Not found */
  if (idx >= state->nfds) {
    /* Do nothing */
    if (!readable && !writable) {
      return;
    }

    /* Add */
    state->nfds++;
    state->fds = realloc(state->fds, sizeof(*state->fds) * state->nfds);
  } else {
    /* Remove */
    if (!readable && !writable) {
      memmove(&state->fds[idx], &state->fds[idx+1],
        sizeof(*state->fds) * (state->nfds - idx - 1));
      state->nfds--;
      return;
    }
  }

  /* Update Poll Events (including on Add) */
  state->fds[idx].fd     = socket_fd;
  state->fds[idx].events = 0;
  if (readable) {
    state->fds[idx].events |= POLLIN;
  }
  if (writable) {
    state->fds[idx].events |= POLLOUT;
  }
}

void process(dnsstate_t *state)
{
  struct timeval tv;

  while (1) {
    int            rv;
    int            timeout;
    size_t         i;
    struct pollfd *fds;
    size_t         nfds;

    /* Since we don't have any other program state to wait on, we'll just
     * stop looping when we know there are no remaining queries, which is
     * easily indicated by ares_timeout() returning NULL when maxtv is NULL */
    if (ares_timeout(state->channel, NULL, &tv) == NULL) {
      break;
    }

    timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;

    rv = poll(state->fds, state->nfds, timeout);
    if (rv < 0) {
      continue;
    } else if (rv == 0) {
      /* Process timeouts */
      ares_process_fd(state->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
      continue;
    }

    /* Duplicate fds structure as calling into ares_process_fd() may manipulate
     * the one contained in state */
    nfds = state->nfds;
    fds  = malloc(sizeof(*fds) * nfds);
    memcpy(fds, state->fds, sizeof(*fds) * nfds);

    for (i=0; i<nfds; i++) {
      if (fds[i].revents == 0) {
        continue;
      }

      /* Notify about read/write events per FD */
      ares_process_fd(state->channel,
        (fds[i].revents & (POLLERR|POLLHUP|POLLIN))?fds[i].fd:ARES_SOCKET_BAD,
        (fds[i].revents & POLLOUT)?fds[i].fd:ARES_SOCKET_BAD);
    }

    free(fds);
  }
}

/* Callback that is called when DNS query is finished */
static void addrinfo_cb(void *arg, int status, int timeouts,
                        struct ares_addrinfo *result)
{
  (void)arg; /* Example does not use user context */
  printf("Result: %s, timeouts: %d\n", ares_strerror(status), timeouts);

  if (result) {
    struct ares_addrinfo_node *node;
    for (node = result->nodes; node != NULL; node = node->ai_next) {
      char        addr_buf[64] = "";
      const void *ptr          = NULL;
      if (node->ai_family == AF_INET) {
        const struct sockaddr_in *in_addr =
          (const struct sockaddr_in *)((void *)node->ai_addr);
        ptr = &in_addr->sin_addr;
      } else if (node->ai_family == AF_INET6) {
        const struct sockaddr_in6 *in_addr =
          (const struct sockaddr_in6 *)((void *)node->ai_addr);
        ptr = &in_addr->sin6_addr;
      } else {
        continue;
      }
      ares_inet_ntop(node->ai_family, ptr, addr_buf, sizeof(addr_buf));
      printf("Addr: %s\n", addr_buf);
    }
  }
  ares_freeaddrinfo(result);
}

int main(int argc, char **argv)
{
  dnsstate_t                 state;
  struct ares_options        options;
  int                        optmask = 0;
  struct ares_addrinfo_hints hints;

  memset(&state, 0, sizeof(state));

  if (argc != 2) {
    printf("Usage: %s domain\n", argv[0]);
    return 1;
  }

  /* Initialize library */
  ares_library_init(ARES_LIB_INIT_ALL);

  /* Enable sock state callbacks, we should not use ares_fds() or ares_getsock()
   * in modern implementations. */
  memset(&options, 0, sizeof(options));
  optmask                   |= ARES_OPT_SOCK_STATE_CB;
  options.sock_state_cb      = sock_state_cb;
  options.sock_state_cb_data = &state;

  /* Initialize channel to run queries, a single channel can accept unlimited
   * queries */
  if (ares_init_options(&state.channel, &options, optmask) != ARES_SUCCESS) {
    printf("c-ares initialization issue\n");
    return 1;
  }

  /* Perform an IPv4 and IPv6 request for the provided domain name */
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_flags  = ARES_AI_CANONNAME;
  ares_getaddrinfo(state.channel, argv[1], NULL, &hints, addrinfo_cb,
                   NULL /* user context not specified */);

  /* Wait until no more requests are left to be processed */
  process(&state);

  /* Cleanup */
  ares_destroy(state.channel);
  free(state.fds);

  ares_library_cleanup();
  return 0;
}

Compilation:

cc -I/usr/local/include -o example_ss example_ss.c -Wl,-rpath /usr/local/lib -lcares