summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2006-08-05 20:48:58 +0000
committerStef Walter <stef@memberwebs.com>2006-08-05 20:48:58 +0000
commit2d975d635f1903a5a5b84ff808b0311d431f9e25 (patch)
tree04dcb3842e05dadd22764e56fcadc17f0072c632 /common
parent2b77de36782f4906b20b45d695524bbe48c731fc (diff)
Added asynchronous DNS resolver. See #47
Diffstat (limited to 'common')
-rw-r--r--common/async-resolver.c322
-rw-r--r--common/async-resolver.h16
-rw-r--r--common/server-mainloop.h2
-rw-r--r--common/sock-any.c35
-rw-r--r--common/sock-any.h17
5 files changed, 381 insertions, 11 deletions
diff --git a/common/async-resolver.c b/common/async-resolver.c
new file mode 100644
index 0000000..c734bff
--- /dev/null
+++ b/common/async-resolver.c
@@ -0,0 +1,322 @@
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "async-resolver.h"
+#include "server-mainloop.h"
+
+/* -----------------------------------------------------------------------------
+ * THREAD COMMUNICATION
+ */
+
+#define TSIGNAL_UNITITIALIZED { -1 -1 }
+
+static int
+tsignal_init(int* sig)
+{
+ if(pipe(sig) == -1)
+ return -1;
+ fcntl(sig[0], F_SETFL, fcntl(sig[0], F_GETFL, 0) | O_NONBLOCK);
+ return 0;
+}
+
+static int
+tsignal_get_fd(int* sig)
+{
+ return sig[0];
+}
+
+static void
+tsignal_wake(int* sig)
+{
+ write(sig[1], "1", 1);
+}
+
+static void
+tsignal_clear(int* sig)
+{
+ char buf[16];
+ while(read(sig[0], buf, sizeof(buf)) > 0);
+}
+
+static void
+tsignal_wait(int* sig, struct timeval* tv)
+{
+ fd_set watch;
+ FD_ZERO(&watch);
+ FD_SET(sig[0], &watch);
+ select(sig[0], &watch, NULL, NULL, tv);
+}
+
+static void
+tsignal_uninit(int* sig)
+{
+ if(sig[1] != -1)
+ close(sig[1]);
+ sig[1] = -1;
+ if(sig[0] != -1)
+ close(sig[0]);
+ sig[0] = -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * RESOLVER
+ */
+
+typedef struct _resolve_request
+{
+ char hostname[256];
+ char servname[256];
+ async_resolve_callback cb;
+ void *arg;
+
+ int gaierr;
+ int errn;
+ struct addrinfo *ai;
+
+ struct _resolve_request *next;
+}
+resolve_request;
+
+/* The queues */
+static int res_quit = 0;
+static resolve_request* res_requests = NULL;
+static resolve_request* res_done = NULL;
+
+/* Thread communication */
+static pthread_t res_thread = 0;
+static pthread_mutex_t res_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int res_request_signal[2] = TSIGNAL_UNITITIALIZED;
+static int res_done_signal[2] = TSIGNAL_UNITITIALIZED;
+
+static void*
+resolver_thread(void* arg)
+{
+ resolve_request* req;
+ resolve_request* r;
+ struct timeval tv;
+
+ while(!res_quit)
+ {
+ pthread_mutex_lock(&res_mutex);
+
+ /* Dig out any requests */
+ req = res_requests;
+ if(req)
+ {
+ res_requests = req->next;
+ req->next = NULL;
+ }
+
+ pthread_mutex_unlock(&res_mutex);
+
+ /* No requests, wait for a request */
+ if(!req)
+ {
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ tsignal_wait(res_request_signal, &tv);
+ tsignal_clear(res_request_signal);
+ continue;
+ }
+
+ /* The actual resolve */
+ req->gaierr = getaddrinfo(req->hostname, req->servname[0] ? req->servname : NULL,
+ NULL, &req->ai);
+ req->errn = errno;
+
+ /* A timeout */
+ if(!req->gaierr && !req->ai)
+ {
+ req->gaierr = EAI_SYSTEM;
+ req->errn = ETIMEDOUT;
+ }
+
+ /* Append the result to done */
+ pthread_mutex_lock(&res_mutex);
+
+ if(!res_done)
+ {
+ res_done = req;
+ }
+ else
+ {
+ r = res_done;
+ while(r->next)
+ r = r->next;
+ r->next = req;
+ }
+
+ pthread_mutex_unlock(&res_mutex);
+
+ /* Tell the main thread to check outbound */
+ tsignal_wake(res_done_signal);
+ }
+
+ return NULL;
+}
+
+static void
+resolver_done(int fd, int type, void* arg)
+{
+ resolve_request* req;
+ resolve_request* r;
+
+ tsignal_clear(res_done_signal);
+
+ pthread_mutex_lock(&res_mutex);
+
+ req = res_done;
+ res_done = NULL;
+
+ pthread_mutex_unlock(&res_mutex);
+
+ while(req)
+ {
+ /* Send off the result */
+ errno = req->errn;
+ (req->cb)(req->gaierr, req->ai, req->arg);
+
+ /* And free it all */
+ r = req->next;
+ if(req->ai)
+ freeaddrinfo(req->ai);
+ free(req);
+
+ req = r;
+ }
+}
+
+int
+async_resolver_init()
+{
+ int r;
+
+ /* The signal pipes */
+ if(tsignal_init(res_request_signal) < 0)
+ return -1;
+ if(tsignal_init(res_done_signal) < 0)
+ return -1;
+
+ if(server_watch(tsignal_get_fd(res_done_signal), SERVER_READ, resolver_done, NULL) == -1)
+ return -1;
+
+ r = pthread_create(&res_thread, NULL, resolver_thread, NULL);
+ if(r != 0)
+ {
+ res_thread = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+async_resolver_queue(const char* hostname, const char* servname,
+ async_resolve_callback cb, void* arg)
+{
+ resolve_request* req;
+ resolve_request* r;
+ char* t;
+
+ req = calloc(1, sizeof(resolve_request));
+ if(!req)
+ {
+ /* All errors go to callback */
+ (cb)(EAI_MEMORY, NULL, arg);
+ return;
+ }
+
+ req->cb = cb;
+ req->arg = arg;
+
+ strncpy(req->hostname, hostname, sizeof(req->hostname));
+ req->hostname[sizeof(req->hostname) - 1] = 0;
+
+ /* A colon and we try to split */
+ t = strchr(req->hostname, ':');
+ if(t)
+ {
+ *t = 0;
+ strncpy(req->servname, t + 1, sizeof(req->servname));
+ }
+
+ if(servname && !req->servname[0])
+ strncpy(req->servname, servname, sizeof(req->servname));
+ req->servname[sizeof(req->servname) - 1] = 0;
+
+ /* Append the result to requests */
+ pthread_mutex_lock(&res_mutex);
+
+ if(!res_requests)
+ {
+ res_requests = req;
+ }
+ else
+ {
+ r = res_requests;
+ while(r->next)
+ r = r->next;
+ r->next = req;
+ }
+
+ pthread_mutex_unlock(&res_mutex);
+
+ tsignal_wake(res_request_signal);
+}
+
+void
+async_resolver_uninit()
+{
+ resolve_request* req;
+
+ /* No more responses from this point on */
+ if(tsignal_get_fd(res_done_signal) != -1)
+ server_unwatch(tsignal_get_fd(res_done_signal));
+
+ pthread_mutex_lock(&res_mutex);
+
+ while(res_requests)
+ {
+ req = res_requests->next;
+ if(res_requests->ai)
+ freeaddrinfo(res_requests->ai);
+ free(res_requests);
+ res_requests = req;
+ }
+
+ while(res_done)
+ {
+ req = res_done->next;
+ if(res_done->ai)
+ freeaddrinfo(res_done->ai);
+ free(res_done);
+ res_done = req;
+ }
+
+ pthread_mutex_unlock(&res_mutex);
+
+ /* Wake up the resolver thread */
+ res_quit = 1;
+ tsignal_uninit(res_request_signal);
+
+ /* Wait for it to finish */
+ if(res_thread)
+ {
+ pthread_join(res_thread, NULL);
+ res_thread = 0;
+ }
+
+ /* And close up the signals in the other direction */
+ tsignal_uninit(res_done_signal);
+}
diff --git a/common/async-resolver.h b/common/async-resolver.h
new file mode 100644
index 0000000..6b31ff4
--- /dev/null
+++ b/common/async-resolver.h
@@ -0,0 +1,16 @@
+
+#ifndef __ASYNC_RESOLVER_H__
+#define __ASYNC_RESOLVER_H__
+
+#include <netdb.h>
+
+typedef void (*async_resolve_callback)(int ecode, struct addrinfo* ai, void* arg);
+
+int async_resolver_init();
+void async_resolver_uninit();
+
+void async_resolver_queue(const char* hostname, const char* servname,
+ async_resolve_callback cb, void* arg);
+
+
+#endif /* __ASYNC_RESOLVER_H__ */
diff --git a/common/server-mainloop.h b/common/server-mainloop.h
index 098ef44..788f3ae 100644
--- a/common/server-mainloop.h
+++ b/common/server-mainloop.h
@@ -4,8 +4,6 @@
#include <stdint.h>
-/* TODO: Prefix functions with svr */
-
#define SERVER_READ 0x01
#define SERVER_WRITE 0x02
diff --git a/common/sock-any.c b/common/sock-any.c
index 1938d5d..bb207ea 100644
--- a/common/sock-any.c
+++ b/common/sock-any.c
@@ -246,6 +246,7 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts)
{
struct addrinfo* res;
int port = 0;
+ int family = 0;
t = NULL;
l = strlen(addr);
@@ -273,13 +274,33 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts)
break;
}
- /* Try and resolve the domain name */
- if(getaddrinfo(buf, NULL, NULL, &res) != 0 || !res)
- break;
+ if(!(opts & SANY_OPT_NORESOLV))
+ {
+ /* Try and resolve the domain name */
+ if(getaddrinfo(buf, NULL, NULL, &res) != 0 || !res)
+ break;
- memcpy(&(any->s.a), res->ai_addr, sizeof(struct sockaddr));
- any->namelen = res->ai_addrlen;
- freeaddrinfo(res);
+ memcpy(&(any->s.a), res->ai_addr, sizeof(struct sockaddr));
+ any->namelen = res->ai_addrlen;
+ family = any->s.a.sa_family;
+ freeaddrinfo(res);
+ }
+ else
+ {
+ family = SANY_AF_DNS;
+#ifdef HAVE_INET6
+ if(opt & SANY_OPT_DEFINET6)
+ {
+ any->s.a.sa_family = AF_INET6;
+ any->namelen = sizeof(any->s.in6);
+ }
+ else
+#endif
+ {
+ any->s.a.sa_family = AF_INET;
+ any->namelen = sizeof(any->s.in);
+ }
+ }
port = htons((unsigned short)(port <= 0 ? defport : port));
@@ -295,7 +316,7 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts)
#endif
};
- return any->s.a.sa_family;
+ return family;
}
while(0);
diff --git a/common/sock-any.h b/common/sock-any.h
index 31cb13b..7df54e1 100644
--- a/common/sock-any.h
+++ b/common/sock-any.h
@@ -64,6 +64,9 @@ struct sockaddr_any
#define SANY_LEN(any) ((any).namelen)
#define SANY_TYPE(any) ((any).s.a.sa_family)
+/* -------------------------------------------------------------------------- */
+
+/* Returns AF_XXX family type or -1 */
int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts);
/* The default port to fill in when no IP/IPv6 port specified */
@@ -76,15 +79,25 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts);
#define SANY_OPT_DEFLOCAL 0x00100000
/* When only port specified default to IPv6 */
-#ifdef HAVE_INET6
#define SANY_OPT_DEFINET6 0x00200000
-#endif
+/* Don't resolve host name */
+#define SANY_OPT_NORESOLV 0x01000000
+
+/* The family type returned when resolving is needed */
+#define SANY_AF_DNS 0x01000000
+
+/* -------------------------------------------------------------------------- */
+
+/* Returns -1 when failed */
int sock_any_ntop(const struct sockaddr_any* any, char* addr, size_t addrlen, int opts);
/* Don't print or compare the port */
#define SANY_OPT_NOPORT 0x01000000
+/* -------------------------------------------------------------------------- */
+
+/* Returns 0 for equal */
int sock_any_cmp(const struct sockaddr_any* a1, const struct sockaddr_any* a2, int opts);
#endif /* __SOCK_ANY_H__ */