Last active
April 10, 2023 17:54
-
-
Save ammarfaizi2/d1713ca9200da037c366c7cd80bf8a58 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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