Skip to content

Instantly share code, notes, and snippets.

@ammarfaizi2
Last active April 10, 2023 17:54
Show Gist options
  • Save ammarfaizi2/d1713ca9200da037c366c7cd80bf8a58 to your computer and use it in GitHub Desktop.
Save ammarfaizi2/d1713ca9200da037c366c7cd80bf8a58 to your computer and use it in GitHub Desktop.
#include <ares.h>
#include <ares_dns.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/select.h>
struct ares_thread {
ares_channel channel;
fd_set readers;
fd_set writers;
uint32_t queue;
thrd_t thread;
mtx_t lock;
cnd_t cond;
volatile bool should_stop;
volatile bool need_wakeup;
};
static void callback(void *arg, int status, int timeouts,
struct ares_addrinfo *result)
{
struct ares_addrinfo_node *nodes, *n;
if (status != ARES_SUCCESS) {
printf("error %d = %s\n", status, ares_strerror(status));
return;
}
printf("name = %s; status = %d; timeouts = %d; \n", result->name,
status, timeouts);
nodes = result->nodes;
for (n = nodes; n; n = n->ai_next) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)n->ai_addr;
struct sockaddr_in *addr4 = (struct sockaddr_in *)n->ai_addr;
char addr[INET6_ADDRSTRLEN];
void *paddr;
if (n->ai_family == AF_INET6)
paddr = &addr6->sin6_addr;
else if (n->ai_family == AF_INET)
paddr = &addr4->sin_addr;
else
continue;
ares_inet_ntop(n->ai_family, paddr, addr, sizeof(addr));
printf(" %s = %s\n", result->name, addr);
}
ares_freeaddrinfo(result);
(void)arg;
}
/*
* __must_hold(&at->lock)
*/
static void ares_worker_process_queue(struct ares_thread *at)
{
int ret, nfds;
mtx_unlock(&at->lock);
while (1) {
FD_ZERO(&at->readers);
FD_ZERO(&at->writers);
nfds = ares_fds(at->channel, &at->readers, &at->writers);
if (nfds == 0)
break;
ret = select(nfds, &at->readers, &at->writers, NULL, NULL);
if (ret == -1) {
perror("select");
break;
}
ares_process(at->channel, &at->readers, &at->writers);
}
mtx_lock(&at->lock);
}
static bool ares_thread_wait_queue(struct ares_thread *at)
{
while (at->queue == 0) {
if (at->should_stop)
return false;
at->need_wakeup = true;
cnd_wait(&at->cond, &at->lock);
at->need_wakeup = false;
}
return true;
}
int ares_thread_worker(void *arg)
{
struct ares_thread *at = arg;
mtx_lock(&at->lock);
while (1) {
if (!ares_thread_wait_queue(at))
break;
ares_worker_process_queue(at);
at->queue--;
}
mtx_unlock(&at->lock);
return 0;
}
static void destroy_ares_thread(struct ares_thread *at)
{
mtx_lock(&at->lock);
at->should_stop = true;
if (at->need_wakeup)
cnd_signal(&at->cond);
mtx_unlock(&at->lock);
thrd_join(at->thread, NULL);
cnd_destroy(&at->cond);
mtx_destroy(&at->lock);
ares_destroy(at->channel);
free(at);
}
static struct ares_thread *create_ares_thread(void)
{
struct ares_thread *ret;
int err;
ret = calloc(1u, sizeof(*ret));
if (!ret)
return NULL;
err = ares_init(&ret->channel);
if (err != ARES_SUCCESS)
goto out_free_ret;
err = mtx_init(&ret->lock, mtx_plain);
if (err != thrd_success)
goto out_destroy_ares;
err = cnd_init(&ret->cond);
if (err != thrd_success)
goto out_destroy_mtx;
err = thrd_create(&ret->thread, ares_thread_worker, ret);
if (err != thrd_success)
goto out_destroy_cnd;
return ret;
out_destroy_cnd:
cnd_destroy(&ret->cond);
out_destroy_mtx:
mtx_destroy(&ret->lock);
out_destroy_ares:
ares_destroy(ret->channel);
out_free_ret:
free(ret);
return NULL;
}
static void t_ares_getaddrinfo(struct ares_thread *at, const char *node,
const char *service,
const struct ares_addrinfo_hints *hints,
ares_addrinfo_callback callback, void *arg)
{
mtx_lock(&at->lock);
ares_getaddrinfo(at->channel, node, service, hints, callback, arg);
at->queue++;
if (at->need_wakeup)
cnd_signal(&at->cond);
mtx_unlock(&at->lock);
}
int main(void)
{
struct ares_addrinfo_hints hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
.ai_flags = ARES_AI_CANONNAME,
};
struct ares_thread *at;
int ret;
ret = ares_library_init(ARES_LIB_INIT_ALL);
if (ret != ARES_SUCCESS) {
fprintf(stderr, "ares_library_init: %s\n", ares_strerror(ret));
return 1;
}
at = create_ares_thread();
if (!at) {
ares_library_cleanup();
return 1;
}
t_ares_getaddrinfo(at, "fb.com", NULL, &hints, callback, NULL);
t_ares_getaddrinfo(at, "google.com", NULL, &hints, callback, NULL);
t_ares_getaddrinfo(at, "youtube.com", NULL, &hints, callback, NULL);
t_ares_getaddrinfo(at, "twitter.com", NULL, &hints, callback, NULL);
t_ares_getaddrinfo(at, "localhost", NULL, &hints, callback, NULL);
t_ares_getaddrinfo(at, "intel.com", NULL, &hints, callback, NULL);
t_ares_getaddrinfo(at, "intern.facebook.com", NULL, &hints, callback, NULL);
t_ares_getaddrinfo(at, "kernel.org", NULL, &hints, callback, NULL);
t_ares_getaddrinfo(at, "github.com", NULL, &hints, callback, NULL);
t_ares_getaddrinfo(at, "gitlab.com", NULL, &hints, callback, NULL);
destroy_ares_thread(at);
ares_library_cleanup();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment