--- /dev/null
+/* GNIO - GLib Network Layer of GIO
+ *
+ * Copyright (C) 2008 Christian Kellner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org>
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+
+#include "ginetaddress.h"
+#include "ginet4address.h"
+#include "ginet6address.h"
+#include "gresolver.h"
+#include "gnioerror.h"
+
+G_DEFINE_TYPE (GResolver, g_resolver, G_TYPE_OBJECT);
+
+#if defined(HAVE_GETHOSTBYNAME_R_GLIB_MUTEX) || defined(HAVE_GETADDRINFO_GLIB_MUTEX)
+# ifndef G_THREADS_ENABLED
+# error Using GLib Mutex but thread are not enabled.
+# endif
+G_LOCK_DEFINE (dnslock);
+#endif
+
+#if !defined(HAVE_GETADDRINFO)
+static GList *
+hostent2list (const struct hostent *he)
+{
+ GList *list = NULL;
+ int i;
+
+ g_return_val_if_fail (he != NULL, NULL);
+
+ for (i = 0; he->h_addr_list[i]; i++)
+ {
+ GInetAddress *address = NULL;
+
+ if (he->h_addrtype == AF_INET)
+ address = G_INET_ADDRESS (g_inet4_address_from_bytes ((guint8 *) he->h_addr_list[i]));
+ else if (he->h_addrtype == AF_INET6)
+ address = G_INET_ADDRESS (g_inet6_address_from_bytes ((guint8 *) he->h_addr_list[i]));
+
+ list = g_list_prepend (list, address);
+ }
+
+ return list;
+}
+#endif
+
+#if defined(HAVE_GETADDRINFO)
+static void
+g_io_error_from_addrinfo (GError** error, int err)
+{
+ GIOErrorEnum code = G_IO_ERROR_FAILED;
+ const gchar *message = NULL;
+
+ if (error == NULL)
+ return;
+
+ switch (err)
+ {
+ case EAI_NONAME:
+ code = G_IO_ERROR_RESOLVER_NOT_FOUND;
+ break;
+ case EAI_NODATA:
+ code = G_IO_ERROR_RESOLVER_NO_DATA;
+ break;
+ default:
+ g_warning ("unknown getaddrinfo() error code encountered");
+ }
+
+ if (message == NULL)
+ {
+ /* FIXME: is gai_strerror() thread-safe? */
+ message = gai_strerror (err);
+ }
+
+ *error = g_error_new_literal (G_IO_ERROR, code, message);
+}
+#endif
+
+static GList *
+g_resolver_get_host_by_name (GResolver *resolver, const gchar *hostname, GError **error)
+{
+ GList *list = NULL;
+
+#if defined(HAVE_GETADDRINFO)
+ {
+ struct addrinfo hints;
+ struct addrinfo *res = NULL, *i;
+ int rv;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_socktype = SOCK_STREAM;
+
+#ifdef HAVE_GETADDRINFO_GLIB_MUTEX
+ G_LOCK (dnslock);
+#endif
+
+ if ((rv = getaddrinfo (hostname, NULL, &hints, &res)))
+ g_io_error_from_addrinfo (error, rv);
+ else
+ for (i = res; i != NULL; i = i->ai_next)
+ {
+ if (i->ai_family == PF_INET)
+ list = g_list_prepend (list, g_inet4_address_from_bytes ((guint8 *) &(((struct sockaddr_in *) i->ai_addr)->sin_addr.s_addr)));
+ else if (i->ai_family == PF_INET6)
+ list = g_list_prepend (list, g_inet6_address_from_bytes ((guint8 *) &(((struct sockaddr_in *) i->ai_addr)->sin_addr.s_addr)));
+ }
+
+ if (res)
+ freeaddrinfo (res);
+
+#ifdef HAVE_GETADDRINFO_GLIB_MUTEX
+ G_UNLOCK (dnslock);
+#endif
+ }
+#elif defined(HAVE_GETHOSTBYNAME_THREADSAFE)
+ {
+ struct hostent *he = gethostbyname (hostname);
+
+ if (!he)
+ g_io_error_from_errno (error);
+ else
+ list = hostent2list (he);
+ }
+#elif defined(HAVE_GETHOSTBYNAME_R_GLIBC)
+ {
+ struct hostent result, *he;
+ gsize len = 1024;
+ gchar *buf = g_new (gchar, len);
+ gint rv, herr;
+
+ while ((rv = gethostbyname_r (hostname, &result, buf, len, &he, &herr)) == ERANGE)
+ {
+ len *= 2;
+ buf = g_renew (gchar, buf, len);
+ }
+
+ if (!rv)
+ list = hostent2list (he);
+
+ g_free (buf);
+ }
+#elif defined(HAVE_GETHOSTBYNAME_R_SOLARIS)
+ {
+ struct hostent result, *he;
+ gsize len = 8192;
+ char *buf = NULL;
+
+ do
+ {
+ buf = g_renew (gchar, buf, len);
+ errno = 0;
+ he = gethostbyname_r (hostname, &result, buf, len, &h_errno);
+ len += 1024;
+ }
+ while (errno == ERANGE);
+
+ if (he)
+ list = hostent2list (&result);
+
+ g_free (buf);
+ }
+#elif defined(HAVE_GETHOSTBYNAME_R_HPUX)
+ {
+ struct hostent he;
+ struct hostent_data buf;
+ int rv;
+
+ rv = gethostbyname_r (hostname, &he, &buf);
+
+ if (!rv)
+ list = hostent2list (&he);
+ }
+#else
+ {
+ struct hostent *he;
+
+#ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX
+ G_LOCK (dnslock);
+#endif
+
+ he = gethostbyname (hostname);
+ list = hostent2list (he);
+
+#ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX
+ G_UNLOCK (dnslock);
+#endif
+ }
+#endif
+
+ if (list)
+ list = g_list_reverse (list);
+
+ return list;
+}
+
+static void
+g_resolver_class_init (GResolverClass *klass)
+{
+ GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
+}
+
+static void
+g_resolver_init (GResolver *address)
+{
+
+}
+
+typedef struct {
+ GList *list;
+ const gchar *host;
+} ResolveListData;
+
+static void
+resolve_list_thread (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ ResolveListData *op;
+ GError *error = NULL;
+
+ op = g_simple_async_result_get_op_res_gpointer (res);
+
+ op->list = g_resolver_resolve_list (G_RESOLVER (object), op->host, cancellable, &error);
+
+ if (op->list == NULL)
+ {
+ g_simple_async_result_set_from_error (res, error);
+ g_error_free (error);
+ }
+}
+
+GInetAddress *
+g_resolver_resolve (GResolver *resolver,
+ const char *host,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GList *list;
+ GInetAddress *address;
+
+ list = g_resolver_get_host_by_name (resolver, host, error);
+
+ if (!list)
+ return NULL;
+
+ address = G_INET_ADDRESS (g_object_ref (g_list_first (list)->data));
+
+ g_list_foreach (list, (GFunc) g_object_unref, NULL);
+
+ g_list_free (list);
+
+ return address;
+}
+
+void
+g_resolver_resolve_async (GResolver *resolver,
+ const char *host,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+ ResolveListData *op;
+
+ op = g_new (ResolveListData, 1);
+
+ res = g_simple_async_result_new (G_OBJECT (resolver), callback, user_data, g_resolver_resolve_list_async);
+
+ g_simple_async_result_set_op_res_gpointer (res, op, g_free);
+
+ op->host = host;
+
+ g_simple_async_result_run_in_thread (res, resolve_list_thread, G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (res);
+}
+
+GInetAddress *
+g_resolver_resolve_finish (GResolver *resolver,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result);
+ ResolveListData *op;
+ GInetAddress *address;
+
+ g_warn_if_fail (g_simple_async_result_get_source_tag (res) == g_resolver_resolve_list_async);
+
+ op = g_simple_async_result_get_op_res_gpointer (res);
+
+ g_simple_async_result_propagate_error (res, error);
+
+ if (op->list == NULL)
+ return NULL;
+
+ address = G_INET_ADDRESS (g_object_ref (g_list_first (op->list)->data));
+
+ g_list_foreach (op->list, (GFunc) g_object_unref, NULL);
+
+ g_list_free (op->list);
+
+ return address;
+}
+
+GList *
+g_resolver_resolve_list (GResolver *resolver,
+ const char *host,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_resolver_get_host_by_name (resolver, host, error);
+}
+
+void
+g_resolver_resolve_list_async (GResolver *resolver,
+ const char *host,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+ ResolveListData *op;
+
+ op = g_new (ResolveListData, 1);
+
+ res = g_simple_async_result_new (G_OBJECT (resolver), callback, user_data, g_resolver_resolve_list_async);
+
+ g_simple_async_result_set_op_res_gpointer (res, op, g_free);
+
+ op->host = host;
+
+ g_simple_async_result_run_in_thread (res, resolve_list_thread, G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (res);
+}
+
+GList *
+g_resolver_resolve_list_finish (GResolver *resolver,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result);
+ ResolveListData *op;
+
+ g_warn_if_fail (g_simple_async_result_get_source_tag (res) == g_resolver_resolve_list_async);
+
+ op = g_simple_async_result_get_op_res_gpointer (res);
+
+ g_simple_async_result_propagate_error (res, error);
+
+ return op->list;
+}
+
--- /dev/null
+#ifndef G_RESOLVER_H
+#define G_RESOLVER_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "ginetaddress.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_RESOLVER (g_resolver_get_type ())
+#define G_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOLVER, GResolver))
+#define G_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOLVER, GResolverClass))
+#define G_IS_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOLVER))
+#define G_IS_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOLVER))
+#define G_RESOLVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOLVER, GResolver))
+
+typedef struct _GResolver GResolver;
+typedef struct _GResolverClass GResolverClass;
+
+struct _GResolver
+{
+ GObject parent;
+};
+
+struct _GResolverClass
+{
+ GObjectClass parent_class;
+};
+
+GType g_resolver_get_type (void) G_GNUC_CONST;
+
+GInetAddress * g_resolver_resolve (GResolver *resolver,
+ const char *host,
+ GCancellable *cancellable,
+ GError **error);
+
+void g_resolver_resolve_async (GResolver *resolver,
+ const char *host,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+GInetAddress * g_resolver_resolve_finish (GResolver *resolver,
+ GAsyncResult *result,
+ GError **error);
+
+GList * g_resolver_resolve_list (GResolver *resolver,
+ const char *host,
+ GCancellable *cancellable,
+ GError **error);
+
+void g_resolver_resolve_list_async (GResolver *resolver,
+ const char *host,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+GList * g_resolver_resolve_list_finish (GResolver *resolver,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* G_RESOLVER_H */
+