diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9662dcf..9d8ed68 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -83,10 +83,6 @@ if (SQLITE_SUPPORT)
target_link_libraries(mod_auth_sqlite sqlite3 utils)
target_link_libraries(uhub-passwd sqlite3 utils)
set_target_properties(mod_auth_sqlite PROPERTIES PREFIX "")
-
- if (UNIX)
- target_link_libraries(uhub pthread)
- endif()
endif()
@@ -121,11 +117,12 @@ target_link_libraries(mod_logging network)
if(UNIX)
add_library(adcclient STATIC ${adcclient_SOURCES})
add_executable(uhub-admin ${PROJECT_SOURCE_DIR}/tools/admin.c)
- target_link_libraries(uhub-admin adcclient adc network utils)
+ target_link_libraries(uhub-admin adcclient adc network utils pthread)
+ target_link_libraries(uhub pthread)
if (ADC_STRESS)
add_executable(adcrush ${PROJECT_SOURCE_DIR}/tools/adcrush.c ${adcclient_SOURCES})
- target_link_libraries(adcrush adcclient adc network utils)
+ target_link_libraries(adcrush adcclient adc network utils pthread)
endif()
endif()
diff --git a/src/network/backend.c b/src/network/backend.c
index e347b3e..678ab9d 100644
--- a/src/network/backend.c
+++ b/src/network/backend.c
@@ -146,6 +146,9 @@ int net_backend_process()
return 0;
}
+ // Process pending DNS results
+ net_dns_process();
+
g_backend->handler.backend_process(g_backend->data, res);
net_cleanup_process(g_backend->cleaner);
diff --git a/src/network/dnsresolver.c b/src/network/dnsresolver.c
new file mode 100644
index 0000000..cbd0091
--- /dev/null
+++ b/src/network/dnsresolver.c
@@ -0,0 +1,383 @@
+/*
+ * uhub - A tiny ADC p2p connection hub
+ * Copyright (C) 2007-2012, Jan Vidar Krey
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "uhub.h"
+
+#define DEBUG_LOOKUP_TIME 1
+#define MAX_CONCURRENT_JOBS 25
+
+static struct net_dns_job* find_and_remove_job(struct net_dns_job* job);
+static struct net_dns_result* find_and_remove_result(struct net_dns_job* job);
+
+struct net_dns_job
+{
+ net_dns_job_cb callback;
+ void* ptr;
+
+ char* host;
+ int af;
+
+#ifdef DEBUG_LOOKUP_TIME
+ struct timeval time_start;
+ struct timeval time_finish;
+#endif
+
+#ifdef POSIX_THREAD_SUPPORT
+ pthread_t thread_handle;
+#endif
+};
+
+struct net_dns_result
+{
+ struct linked_list* addr_list;
+ struct net_dns_job* job;
+};
+
+static void free_job(struct net_dns_job* job)
+{
+ if (job)
+ {
+ hub_free(job->host);
+ hub_free(job);
+ }
+}
+
+// NOTE: Any job manipulating the members of this
+// struct must lock the mutex!
+struct net_dns_subsystem
+{
+ struct linked_list* jobs; // currently running jobs
+ struct linked_list* results; // queue of results that are awaiting being delivered to callback.
+#ifdef POSIX_THREAD_SUPPORT
+ pthread_mutex_t mutex;
+#endif // POSIX_THREAD_SUPPORT
+};
+
+static struct net_dns_subsystem* g_dns = NULL;
+
+void net_dns_initialize()
+{
+ LOG_TRACE("net_dns_initialize()");
+ g_dns = (struct net_dns_subsystem*) hub_malloc_zero(sizeof(struct net_dns_subsystem));
+ g_dns->jobs = list_create();
+ g_dns->results = list_create();
+#ifdef POSIX_THREAD_SUPPORT
+ pthread_mutex_init(&g_dns->mutex, NULL);
+#endif
+}
+
+void net_dns_destroy()
+{
+#ifdef POSIX_THREAD_SUPPORT
+ struct net_dns_job* job;
+ pthread_mutex_lock(&g_dns->mutex);
+ LOG_TRACE("net_dns_destroy(): jobs=%d", (int) list_size(g_dns->jobs));
+ job = (struct net_dns_job*) list_get_first(g_dns->jobs);
+ pthread_mutex_unlock(&g_dns->mutex);
+
+ while (job)
+ {
+ net_dns_job_cancel(job);
+
+ pthread_mutex_lock(&g_dns->mutex);
+ job = (struct net_dns_job*) list_get_first(g_dns->jobs);
+ pthread_mutex_unlock(&g_dns->mutex);
+ }
+#endif
+}
+
+#ifdef POSIX_THREAD_SUPPORT
+static void dummy_free(void* ptr)
+{
+}
+#endif
+
+
+void net_dns_process()
+{
+#ifdef POSIX_THREAD_SUPPORT
+ struct net_dns_result* result;
+ pthread_mutex_lock(&g_dns->mutex);
+ LOG_DUMP("net_dns_process(): jobs=%d, results=%d", (int) list_size(g_dns->jobs), (int) list_size(g_dns->results));
+
+ for (result = (struct net_dns_result*) list_get_first(g_dns->results); result; result = (struct net_dns_result*) list_get_next(g_dns->results))
+ {
+ struct net_dns_job* job = result->job;
+#ifdef DEBUG_LOOKUP_TIME
+ struct timeval time_result;
+ timersub(&result->job->time_finish, &result->job->time_start, &time_result);
+ LOG_TRACE("DNS lookup took %d ms", (time_result.tv_sec * 1000) + (time_result.tv_usec / 1000));
+#endif
+
+ // wait for the work thread to finish
+ pthread_join(job->thread_handle, NULL);
+
+ // callback - should we delete the data immediately?
+ if (job->callback(job, result))
+ {
+ net_dns_result_free(result);
+ }
+ else
+ {
+ /* Caller wants to keep the result data, and
+ * thus needs to call net_dns_result_free() to release it later.
+ * We only clean up the job data here and keep the results intact.
+ */
+ result->job = NULL;
+ free_job(job);
+ }
+ }
+
+ list_clear(g_dns->results, &dummy_free);
+ pthread_mutex_unlock(&g_dns->mutex);
+#endif // POSIX_THREAD_SUPPORT
+}
+
+#ifdef POSIX_THREAD_SUPPORT
+static void* job_thread_resolve_name(void* ptr)
+{
+ struct net_dns_job* job = (struct net_dns_job*) ptr;
+ struct addrinfo hints, *result, *it;
+ struct net_dns_result* dns_results;
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = job->af;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ ret = getaddrinfo(job->host, NULL, &hints, &result);
+ if (ret != 0)
+ {
+ LOG_WARN("getaddrinfo() failed: %s", gai_strerror(ret));
+ return NULL;
+ }
+
+ dns_results = (struct net_dns_result*) hub_malloc(sizeof(struct net_dns_result));
+ dns_results->addr_list = list_create();
+ dns_results->job = job;
+
+ for (it = result; it; it = it->ai_next)
+ {
+ struct ip_addr_encap* ipaddr = hub_malloc_zero(sizeof(struct ip_addr_encap));
+ ipaddr->af = it->ai_family;
+
+ if (it->ai_family == AF_INET)
+ {
+ struct sockaddr_in* addr4 = (struct sockaddr_in*) it->ai_addr;
+ memcpy(&ipaddr->internal_ip_data.in, &addr4->sin_addr, sizeof(struct in_addr));
+ }
+ else if (it->ai_family == AF_INET6)
+ {
+ struct sockaddr_in6* addr6 = (struct sockaddr_in6*) it->ai_addr;
+ memcpy(&ipaddr->internal_ip_data.in6, &addr6->sin6_addr, sizeof(struct in6_addr));
+ }
+ else
+ {
+ LOG_WARN("getaddrinfo() returned result with unknown address family: %d", it->ai_family);
+ hub_free(ipaddr);
+ continue;
+ }
+
+ LOG_WARN("getaddrinfo() - Address (%d): %s", ret++, ip_convert_to_string(ipaddr));
+ list_append(dns_results->addr_list, ipaddr);
+ }
+ freeaddrinfo(result);
+
+#ifdef DEBUG_LOOKUP_TIME
+ gettimeofday(&job->time_finish, NULL);
+#endif
+
+ pthread_mutex_lock(&g_dns->mutex);
+ list_remove(g_dns->jobs, job);
+ list_append(g_dns->results, dns_results);
+ pthread_mutex_unlock(&g_dns->mutex);
+
+ return dns_results;
+}
+#endif
+
+extern struct net_dns_job* net_dns_gethostbyname(const char* host, int af, net_dns_job_cb callback, void* ptr)
+{
+ struct net_dns_job* job = (struct net_dns_job*) hub_malloc_zero(sizeof(struct net_dns_job));
+ job->host = strdup(host);
+ job->af = af;
+ job->callback = callback;
+ job->ptr = ptr;
+
+#ifdef DEBUG_LOOKUP_TIME
+ gettimeofday(&job->time_start, NULL);
+#endif
+
+ // FIXME - scheduling - what about a max number of threads?
+#ifdef POSIX_THREAD_SUPPORT
+ pthread_mutex_lock(&g_dns->mutex);
+ int err = pthread_create(&job->thread_handle, NULL, job_thread_resolve_name, job);
+ if (err)
+ {
+ LOG_WARN("Unable to create thread: (%d) %s", err, strerror(err));
+ free_job(job);
+ job = NULL;
+ }
+ else
+ {
+ list_append(g_dns->jobs, job);
+ }
+ pthread_mutex_unlock(&g_dns->mutex);
+#endif
+ return job;
+}
+
+
+
+extern struct net_dns_job* net_dns_gethostbyaddr(struct ip_addr_encap* ipaddr, net_dns_job_cb callback, void* ptr)
+{
+ struct net_dns_job* job = (struct net_dns_job*) hub_malloc_zero(sizeof(struct net_dns_job));
+// job->host = strdup(addr);
+ job->af = ipaddr->af;
+ job->callback = callback;
+ job->ptr = ptr;
+
+#ifdef POSIX_THREAD_SUPPORT
+// if (pthread_create(&job->thread_handle, NULL, start_job, job))
+// {
+// free_job(job);
+// return NULL;
+// }
+#endif
+ return job;
+}
+
+// NOTE: mutex must be locked first!
+static struct net_dns_job* find_and_remove_job(struct net_dns_job* job)
+{
+ struct net_dns_job* it;
+ for (it = (struct net_dns_job*) list_get_first(g_dns->jobs); it; it = (struct net_dns_job*) list_get_next(g_dns->jobs))
+ {
+ if (it == job)
+ {
+ list_remove(g_dns->jobs, it);
+ return job;
+ }
+ }
+ return NULL;
+}
+
+// NOTE: mutex must be locked first!
+static struct net_dns_result* find_and_remove_result(struct net_dns_job* job)
+{
+ struct net_dns_result* it;
+ for (it = (struct net_dns_result*) list_get_first(g_dns->results); it; it = (struct net_dns_result*) list_get_next(g_dns->results))
+ {
+ if (it->job == job)
+ {
+ list_remove(g_dns->results, it);
+ return it;
+ }
+ }
+ return NULL;
+}
+
+
+extern int net_dns_job_cancel(struct net_dns_job* job)
+{
+#ifdef POSIX_THREAD_SUPPORT
+ int retval = 0;
+ struct net_dns_result* res;
+
+ LOG_TRACE("net_dns_job_cancel(): job=%p, name=%s", job, job->host);
+
+ /*
+ * This function looks up the job in the jobs queue (which contains only active jobs)
+ * If that is found then the thread is cancelled, and the object is deleted.
+ * If the job was not found, that is either because it was an invalid job, or because
+ * it was already finished. At which point it was not deleted.
+ * If the job is already finished, but the result has not been delivered, then this
+ * deletes the result and the job.
+ */
+ pthread_mutex_lock(&g_dns->mutex);
+ if (find_and_remove_job(job))
+ {
+ // job still active - cancel it, then close it.
+ pthread_cancel(job->thread_handle);
+ pthread_join(job->thread_handle, NULL);
+ free_job(job);
+ retval = 1;
+ }
+ else if ((res = find_and_remove_result(job)))
+ {
+ // job already finished - close it.
+ pthread_join(job->thread_handle, NULL);
+ net_dns_result_free(res);
+ }
+ pthread_mutex_unlock(&g_dns->mutex);
+ return retval;
+#endif
+}
+
+extern struct net_dns_result* net_dns_job_sync_wait(struct net_dns_job* job)
+{
+ struct net_dns_result* res = NULL;
+
+ // Wait for job to finish (if not already)
+ // This should make sure the job is removed from jobs and a result is
+ // present in results.
+ pthread_join(job->thread_handle, NULL);
+
+ // Remove the result in order to prevent the callback from being called.
+ pthread_mutex_lock(&g_dns->mutex);
+ res = find_and_remove_result(job);
+ uhub_assert(res != NULL);
+ res->job = NULL;
+ free_job(job);
+ pthread_mutex_unlock(&g_dns->mutex);
+
+ return res;
+}
+
+void* net_dns_job_get_ptr(const struct net_dns_job* job)
+{
+ return job->ptr;
+}
+
+extern size_t net_dns_result_size(const struct net_dns_result* res)
+{
+ return list_size(res->addr_list);
+}
+
+extern struct ip_addr_encap* net_dns_result_first(const struct net_dns_result* res)
+{
+ struct ip_addr_encap* ipaddr = list_get_first(res->addr_list);
+ LOG_TRACE("net_dns_result_first() - Address: %s", ip_convert_to_string(ipaddr));
+ return ipaddr;
+}
+
+extern struct ip_addr_encap* net_dns_result_next(const struct net_dns_result* res)
+{
+ struct ip_addr_encap* ipaddr = list_get_next(res->addr_list);
+ LOG_TRACE("net_dns_result_next() - Address: %s", ip_convert_to_string(ipaddr));
+ return ipaddr;
+}
+
+extern void net_dns_result_free(struct net_dns_result* res)
+{
+ list_clear(res->addr_list, &hub_free);
+ list_destroy(res->addr_list);
+ free_job(res->job);
+ hub_free(res);
+}
diff --git a/src/network/dnsresolver.h b/src/network/dnsresolver.h
new file mode 100644
index 0000000..6937422
--- /dev/null
+++ b/src/network/dnsresolver.h
@@ -0,0 +1,119 @@
+/*
+ * uhub - A tiny ADC p2p connection hub
+ * Copyright (C) 2007-2012, Jan Vidar Krey
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#ifndef HAVE_UHUB_NETWORK_DNS_RESOLVER_H
+#define HAVE_UHUB_NETWORK_DNS_RESOLVER_H
+
+struct net_dns_job;
+struct net_dns_result;
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+/// Initialize the DNS subsystem
+void net_dns_initialize();
+
+/// Shutdown and destroy the DNS subsystem. This will cancel any pending DNS jobs.
+void net_dns_destroy();
+
+/// Process finished DNS lookups.
+void net_dns_process();
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+/**
+ * Callback to be called when the DNS job has finished.
+ * If the name or address could not be resolved to an IP address (host not found, or found but has no address)
+ * then 'result' contains an empty list (@see net_dns_result_size()).
+ * If resolving caused an error then result is NULL.
+ *
+ * After this callback is called the job is considered done, and is freed.
+ *
+ * @param If 1 is returned then result is deleted immediately after the callback,
+ * otherwise the callback becomes owner of the result data which must be freed with net_dns_result_free().
+ */
+typedef int (*net_dns_job_cb)(struct net_dns_job*, const struct net_dns_result* result);
+
+/**
+ * Resolve a hostname.
+ *
+ * @param host the hostname to be resolved.
+ * @param af the indicated address family. Should be AF_INET, AF_INET6 (or AF_UNSPEC - which means both AF_INET and AF_INET6.
+ * @param callback the callback to be called when the hostname has been resolved.
+ * @param ptr A user-defined pointer value.
+ *
+ * @return A resolve job handle if the job has successfully started or NULL if unable to start resolving.
+ */
+extern struct net_dns_job* net_dns_gethostbyname(const char* host, int af, net_dns_job_cb callback, void* ptr);
+
+/**
+ * Perform a reverse DNS lookup for a given IP address.
+ *
+ * @see net_dns_gethostbyname()
+ * @return A resolve job handle if the job has successfully started or NULL if unable to start resolving.
+ */
+extern struct net_dns_job* net_dns_gethostbyaddr(struct ip_addr_encap* ipaddr, net_dns_job_cb callback, void* ptr);
+
+/**
+ * Cancel a DNS lookup job.
+ *
+ * It is only allowed to call this once after a job has been started (@see net_dns_gethostbyname(), @see net_dns_gethostbyaddr())
+ * but before it has finished and delivered a to the callback address (@see net_dns_job_cb).
+ *
+ * @returns 1 if cancelled, or 0 if not cancelled (because the job was not found!)
+ */
+extern int net_dns_job_cancel(struct net_dns_job* job);
+
+/**
+ * Wait in a synchronous manner for a running DNS job to finished and
+ * return the result here.
+ * The job must be started with net_dns_gethostbyaddr/net_dns_gethostbyname
+ * and not finished or cancelled.
+ *
+ * If this function is invoked then the callback function will not be called and
+ * can therefore be NULL.
+ *
+ *
+ * struct net_dns_job* job = net_dns_gethostbyname("www.example.com", AF_INET, NULL, NULL);
+ * struct net_dns_result* net_dns_job_sync_wait(job);
+ *
+ */
+extern struct net_dns_result* net_dns_job_sync_wait(struct net_dns_job* job);
+
+/**
+ * Returns the user specified pointer assigned to the resolving job
+*/
+extern void* net_dns_job_get_ptr(const struct net_dns_job* job);
+
+/// Returns the number of results provided. This is 0 if the host could not be found (or has no matching IP address).
+extern size_t net_dns_result_size(const struct net_dns_result*);
+
+/// Returns the first result (if net_dns_result_size > 0), or NULL if not first result exists.
+extern struct ip_addr_encap* net_dns_result_first(const struct net_dns_result*);
+
+/// Returns the next result or NULL if no next result exists.
+extern struct ip_addr_encap* net_dns_result_next(const struct net_dns_result*);
+
+/// When finished with the results
+extern void net_dns_result_free(struct net_dns_result*);
+
+#endif /* HAVE_UHUB_NETWORK_DNS_RESOLVER_H */
diff --git a/src/network/network.c b/src/network/network.c
index f9b983f..327fe89 100644
--- a/src/network/network.c
+++ b/src/network/network.c
@@ -60,6 +60,9 @@ int net_initialize()
#endif
return -1;
}
+
+ net_dns_initialize();
+
net_stats_initialize();
net_initialized = 1;
return 0;
@@ -94,8 +97,10 @@ int net_destroy()
{
LOG_TRACE("Shutting down network monitor");
+ net_dns_destroy();
+
net_backend_shutdown();
-
+
#ifdef SSL_SUPPORT
net_ssl_library_shutdown();
#endif /* SSL_SUPPORT */
@@ -490,7 +495,6 @@ int net_socket_create(int af, int type, int protocol)
}
}
#endif
-
return sd;
}
diff --git a/src/system.h b/src/system.h
index e608489..e377524 100644
--- a/src/system.h
+++ b/src/system.h
@@ -113,11 +113,13 @@
#define uhub_assert assert
#ifdef __linux__
+#define POSIX_THREAD_SUPPORT
#define USE_EPOLL
#include
#endif
#ifdef BSD_LIKE
+#define POSIX_THREAD_SUPPORT
/*
#define USE_KQUEUE
#include
@@ -279,6 +281,10 @@ typedef unsigned __int64 uint64_t;
#define NEED_GETOPT
#endif
+#ifdef POSIX_THREAD_SUPPORT
+#include
+#endif
+
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(_MSC_VER)
#define PLUGIN_API __declspec(dllexport)
#else
diff --git a/src/uhub.h b/src/uhub.h
index cb90b4c..ad29c3a 100644
--- a/src/uhub.h
+++ b/src/uhub.h
@@ -74,6 +74,7 @@ extern "C" {
#include "network/network.h"
#include "network/connection.h"
+#include "network/dnsresolver.h"
#include "network/ipcalc.h"
#include "network/timeout.h"