Add GResolver, a glib-ish interface to DNS

GResolver provides asynchronous (and synchronous-but-cancellable) APIs
for resolving hostnames, reverse-resolving IP addresses back to
hostnames, and resolving SRV records. Part of #548466.
This commit is contained in:
Dan Winship 2008-12-29 12:53:47 -05:00
parent 68fc055627
commit c94d3f9288
32 changed files with 5412 additions and 10 deletions

View File

@ -974,6 +974,30 @@ if $glib_failed ; then
AC_MSG_ERROR([Could not determine values for AF_INET* constants])
fi
# For gio/libasyncns
if test $glib_native_win32 = no; then
AC_CHECK_FUNCS(strndup setresuid setreuid)
AC_CHECK_HEADERS(sys/prctl.h arpa/nameser_compat.h)
AC_CHECK_FUNC(res_query, ,
[AC_CHECK_LIB(resolv, res_query, [ LIBASYNCNS_LIBADD="-lresolv" ],
[ save_libs="$LIBS"
LIBS="-lresolv $LIBS"
AC_MSG_CHECKING([for res_query in -lresolv (alternate version)])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([[#include <resolv.h>]], [[res_query(0,0,0,0,0)]])],
[ AC_MSG_RESULT(yes)
LIBASYNCNS_LIBADD="-lresolv" ],
[ AC_MSG_RESULT(no)
AC_CHECK_LIB(bind, res_query,
[ LIBASYNCNS_LIBADD="-lbind" ],
[ AC_MSG_ERROR(res_query not found) ] ) ] )
LIBS="$save_libs"
] )
]
)
AC_SUBST(LIBASYNCNS_LIBADD)
fi
dnl
dnl if statfs() takes 2 arguments (Posix) or 4 (Solaris)
dnl
@ -3384,6 +3408,7 @@ gthread/Makefile
gio/Makefile
gio/xdgmime/Makefile
gio/inotify/Makefile
gio/libasyncns/Makefile
gio/fen/Makefile
gio/fam/Makefile
gio/win32/Makefile

View File

@ -43,11 +43,14 @@ IGNORE_HFILES= \
glocalvfs.h \
gnativevolumemonitor.h \
gpollfilemonitor.h \
gthreadedresolver.h \
gunionvolumemonitor.h \
gunixdrive.h \
gunixresolver.h \
gunixvolume.h \
gvolumeprivate.h \
gwin32appinfo.h \
gwin32resolver.h \
inotify-diag.h \
inotify-helper.h \
inotify-kernel.h \
@ -85,7 +88,8 @@ GTKDOC_LIBS = \
$(NULL)
# Extra options to supply to gtkdoc-mkdb
MKDB_OPTIONS = --output-format=xml --sgml-mode --name-space=g
MKDB_OPTIONS = --output-format=xml --sgml-mode --name-space=g \
--ignore-files=libasyncns
# Images to copy into HTML directory
HTML_IMAGES = \

View File

@ -92,10 +92,12 @@
</chapter>
<chapter id="networking">
<title>Networking</title>
<xi:include href="xml/gresolver.xml"/>
<xi:include href="xml/ginetaddress.xml"/>
<xi:include href="xml/gsocketaddress.xml"/>
<xi:include href="xml/ginetsocketaddress.xml"/>
<xi:include href="xml/gunixsocketaddress.xml"/>
<xi:include href="xml/gsrvtarget.xml"/>
</chapter>
<chapter id="utils">
<title>Utilities</title>

View File

@ -1404,3 +1404,55 @@ G_UNIX_SOCKET_ADDRESS_GET_CLASS
<SUBSECTION Private>
g_unix_socket_address_get_type
</SECTION>
<SECTION>
<FILE>gresolver</FILE>
<TITLE>GResolver</TITLE>
GResolver
g_resolver_get_default
g_resolver_set_default
g_resolver_lookup_by_name
g_resolver_lookup_by_name_async
g_resolver_lookup_by_name_finish
g_resolver_free_addresses
g_resolver_lookup_by_address
g_resolver_lookup_by_address_async
g_resolver_lookup_by_address_finish
g_resolver_lookup_service
g_resolver_lookup_service_async
g_resolver_lookup_service_finish
g_resolver_free_targets
<SUBSECTION>
G_RESOLVER_ERROR
GResolverError
<SUBSECTION Standard>
GResolverClass
G_IS_RESOLVER
G_IS_RESOLVER_CLASS
G_RESOLVER
G_RESOLVER_CLASS
G_RESOLVER_GET_CLASS
G_TYPE_RESOLVER
<SUBSECTION Private>
g_resolver_get_type
g_resolver_error_quark
</SECTION>
<SECTION>
<FILE>gsrvtarget</FILE>
<TITLE>GSrvTarget</TITLE>
GSrvTarget
g_srv_target_new
g_srv_target_copy
g_srv_target_free
g_srv_target_get_hostname
g_srv_target_get_port
g_srv_target_get_priority
g_srv_target_get_weight
g_srv_target_get_expires
g_srv_target_array_sort
<SUBSECTION Standard>
G_TYPE_SRV_TARGET
<SUBSECTION Private>
g_srv_target_get_type
</SECTION>

View File

@ -55,6 +55,7 @@ g_native_volume_monitor_get_type
g_output_stream_get_type
g_output_stream_splice_flags_get_type
g_password_save_get_type
g_resolver_get_type
g_seekable_get_type
g_simple_async_result_get_type
g_socket_address_get_type

View File

@ -5,7 +5,7 @@ NULL =
SUBDIRS=
if OS_UNIX
SUBDIRS += xdgmime
SUBDIRS += libasyncns xdgmime
endif
if OS_WIN32_AND_DLL_COMPILATION
@ -125,13 +125,15 @@ endif
if OS_UNIX
appinfo_sources += gdesktopappinfo.c gdesktopappinfo.h
platform_libadd += xdgmime/libxdgmime.la
platform_deps += xdgmime/libxdgmime.la
platform_libadd += libasyncns/libasyncns.la xdgmime/libxdgmime.la
platform_deps += libasyncns/libasyncns.la xdgmime/libxdgmime.la
unix_sources = \
gunixmount.c \
gunixmount.h \
gunixmounts.c \
gunixmounts.h \
gunixresolver.c \
gunixresolver.h \
gunixsocketaddress.c \
gunixvolume.c \
gunixvolume.h \
@ -154,10 +156,12 @@ endif
if OS_WIN32
appinfo_sources += gwin32appinfo.c gwin32appinfo.h
platform_libadd += -lshlwapi -lws2_32
platform_libadd += -lshlwapi -lws2_32 -ldnsapi
win32_sources = \
gwin32mount.c \
gwin32mount.h \
gwin32resolver.c \
gwin32resolver.h \
gwin32volumemonitor.c \
gwin32volumemonitor.h \
$(NULL)
@ -217,10 +221,14 @@ libgio_2_0_la_SOURCES = \
goutputstream.c \
gpollfilemonitor.c \
gpollfilemonitor.h \
gresolver.c \
gseekable.c \
gsimpleasyncresult.c \
gsocketaddress.c \
gsrvtarget.c \
gthemedicon.c \
gthreadedresolver.c \
gthreadedresolver.h \
gunionvolumemonitor.c \
gunionvolumemonitor.h \
gvfs.c \
@ -321,9 +329,11 @@ gio_headers = \
gmountoperation.h \
gnativevolumemonitor.h \
goutputstream.h \
gresolver.h \
gseekable.h \
gsimpleasyncresult.h \
gsocketaddress.h \
gsrvtarget.h \
gthemedicon.h \
gvfs.h \
gvolume.h \

View File

@ -36,7 +36,12 @@
* SECTION:ginetaddress
* @short_description: An IPv4/IPv6 address
*
* #GInetAddress represents an IPv4 or IPv6 internet address.
* #GInetAddress represents an IPv4 or IPv6 internet address. Use
* g_resolver_lookup_by_name() or g_resolver_lookup_by_name_async() to
* look up the #GInetAddress for a hostname. Use
* g_resolver_lookup_by_address() or
* g_resolver_lookup_by_address_async() to look up the hostname for a
* #GInetAddress.
*
* To actually connect to a remote host, you will need a
* #GInetSocketAddress (which includes a #GInetAddress as well as a

View File

@ -64,9 +64,11 @@
#include <gio/gmountoperation.h>
#include <gio/gnativevolumemonitor.h>
#include <gio/goutputstream.h>
#include <gio/gresolver.h>
#include <gio/gseekable.h>
#include <gio/gsimpleasyncresult.h>
#include <gio/gsocketaddress.h>
#include <gio/gsrvtarget.h>
#include <gio/gthemedicon.h>
#include <gio/gvfs.h>
#include <gio/gvolume.h>

View File

@ -834,6 +834,7 @@ g_ask_password_flags_get_type G_GNUC_CONST
g_password_save_get_type G_GNUC_CONST
g_emblem_origin_get_type G_GNUC_CONST
g_socket_family_get_type G_GNUC_CONST
g_resolver_error_get_type G_GNUC_CONST
#endif
#endif
@ -908,3 +909,59 @@ g_socket_address_get_native_size
g_socket_address_to_native
#endif
#endif
#if IN_HEADER(__G_RESOLVER_H__)
#if IN_FILE(__G_RESOLVER_C__)
g_resolver_error_quark
g_resolver_free_addresses
g_resolver_free_targets
g_resolver_get_type G_GNUC_CONST
g_resolver_get_default
g_resolver_set_default
g_resolver_lookup_by_name
g_resolver_lookup_by_name_async
g_resolver_lookup_by_name_finish
g_resolver_lookup_by_address
g_resolver_lookup_by_address_async
g_resolver_lookup_by_address_finish
g_resolver_lookup_service
g_resolver_lookup_service_async
g_resolver_lookup_service_finish
#endif
#endif
#if IN_HEADER(__G_THREADED_RESOLVER_H__)
#if IN_FILE(__G_THREADED_RESOLVER_C__)
g_threaded_resolver_get_type G_GNUC_CONST
#endif
#endif
#if IN_HEADER(__G_UNIX_RESOLVER_H__)
#if IN_FILE(__G_UNIX_RESOLVER_C__)
#ifdef G_OS_UNIX
g_unix_resolver_get_type G_GNUC_CONST
#endif
#endif
#endif
#if IN_HEADER(__G_WIN32_RESOLVER_H__)
#if IN_FILE(__G_WIN32_RESOLVER_C__)
#ifdef G_OS_WIN32
g_win32_resolver_get_type G_GNUC_CONST
#endif
#endif
#endif
#if IN_HEADER(__G_SRV_TARGET_H__)
#if IN_FILE(__G_SRV_TARGET_C__)
g_srv_target_get_type G_GNUC_CONST
g_srv_target_new
g_srv_target_copy
g_srv_target_free
g_srv_target_get_hostname
g_srv_target_get_port
g_srv_target_get_priority
g_srv_target_get_weight
g_srv_target_list_sort
#endif
#endif

View File

@ -476,6 +476,20 @@ typedef enum {
G_EMBLEM_ORIGIN_TAG
} GEmblemOrigin;
/**
* GResolverError:
* @G_RESOLVER_ERROR_NOT_FOUND: the requested name/address/service was not found
* @G_RESOLVER_ERROR_TEMPORARY_FAILURE: the requested information could not be looked up due to a network error or similar problem
* @G_RESOLVER_ERROR_INTERNAL: unknown error
*
* An error code used with %G_RESOLVER_ERROR in a #GError returned
* from a #GResolver routine.
*/
typedef enum {
G_RESOLVER_ERROR_NOT_FOUND,
G_RESOLVER_ERROR_TEMPORARY_FAILURE,
G_RESOLVER_ERROR_INTERNAL
} GResolverError;
/**
* GSocketFamily:

View File

@ -99,12 +99,12 @@ typedef struct _GMemoryOutputStream GMemoryOutputStream;
**/
typedef struct _GMount GMount; /* Dummy typedef */
typedef struct _GMountOperation GMountOperation;
typedef struct _GNetworkAddress GNetworkAddress;
typedef struct _GNetworkService GNetworkService;
typedef struct _GOutputStream GOutputStream;
typedef struct _GResolver GResolver;
typedef struct _GSeekable GSeekable;
typedef struct _GSimpleAsyncResult GSimpleAsyncResult;
typedef struct _GSocketAddress GSocketAddress;
typedef struct _GSrvTarget GSrvTarget;
typedef struct _GThemedIcon GThemedIcon;
typedef struct _GVfs GVfs; /* Dummy typedef */

View File

@ -50,4 +50,36 @@
#endif
G_BEGIN_DECLS
extern struct addrinfo _g_resolver_addrinfo_hints;
GList *_g_resolver_addresses_from_addrinfo (const char *hostname,
struct addrinfo *res,
gint gai_retval,
GError **error);
void _g_resolver_address_to_sockaddr (GInetAddress *address,
struct sockaddr_storage *sa,
gsize *sa_len);
char *_g_resolver_name_from_nameinfo (GInetAddress *address,
const gchar *name,
gint gni_retval,
GError **error);
#if defined(G_OS_UNIX)
GList *_g_resolver_targets_from_res_query (const gchar *rrname,
guchar *answer,
gint len,
gint herr,
GError **error);
#elif defined(G_OS_WIN32)
GList *_g_resolver_targets_from_DnsQuery (const gchar *rrname,
DNS_STATUS status,
DNS_RECORD *results,
GError **error);
#endif
G_END_DECLS
#endif /* __G_NETWORKINGPRIVATE_H__ */

855
gio/gresolver.c Normal file
View File

@ -0,0 +1,855 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#include "config.h"
#include <glib.h>
#include "glibintl.h"
#include "gresolver.h"
#include "gnetworkingprivate.h"
#include "gasyncresult.h"
#include "ginetaddress.h"
#include "ginetsocketaddress.h"
#include "gsimpleasyncresult.h"
#include "gsrvtarget.h"
#ifdef G_OS_UNIX
#include "gunixresolver.h"
#endif
#ifdef G_OS_WIN32
#include "gwin32resolver.h"
#endif
#include <stdlib.h>
#include "gioalias.h"
/**
* SECTION:gresolver
* @short_description: Asynchronous and cancellable DNS resolver
* @include: gio/gio.h
*
* #GResolver provides cancellable synchronous and asynchronous DNS
* resolution, for hostnames (g_resolver_lookup_by_address(),
* g_resolver_lookup_by_name() and their async variants) and SRV
* (service) records (g_resolver_lookup_service()).
**/
/**
* GResolver:
*
* The object that handles DNS resolution. Use g_resolver_get_default()
* to get the default resolver.
*/
G_DEFINE_TYPE (GResolver, g_resolver, G_TYPE_OBJECT)
static void
g_resolver_class_init (GResolverClass *resolver_class)
{
/* Make sure _g_networking_init() has been called */
(void) g_inet_address_get_type ();
/* Initialize _g_resolver_addrinfo_hints */
#ifdef AI_ADDRCONFIG
_g_resolver_addrinfo_hints.ai_flags |= AI_ADDRCONFIG;
#endif
/* These two don't actually matter, they just get copied into the
* returned addrinfo structures (and then we ignore them). But if
* we leave them unset, we'll get back duplicate answers.
*/
_g_resolver_addrinfo_hints.ai_socktype = SOCK_STREAM;
_g_resolver_addrinfo_hints.ai_protocol = IPPROTO_TCP;
}
static void
g_resolver_init (GResolver *resolver)
{
;
}
static GResolver *default_resolver;
/**
* g_resolver_get_default:
*
* Gets the default #GResolver. You should unref it when you are done
* with it. #GResolver may use its reference count as a hint about how
* many threads/processes, etc it should allocate for concurrent DNS
* resolutions.
*
* Return value: the #GResolver.
*
* Since: 2.22
**/
GResolver *
g_resolver_get_default (void)
{
if (!default_resolver)
{
if (g_thread_supported ())
default_resolver = g_object_new (G_TYPE_THREADED_RESOLVER, NULL);
else
{
#if defined(G_OS_UNIX)
default_resolver = g_object_new (G_TYPE_UNIX_RESOLVER, NULL);
#elif defined(G_OS_WIN32)
default_resolver = g_object_new (G_TYPE_WIN32_RESOLVER, NULL);
#endif
}
}
return g_object_ref (default_resolver);
}
/**
* g_resolver_set_default:
* @resolver: the new default #GResolver
*
* Sets @resolver to be the application's default resolver (reffing
* @resolver, and unreffing the previous default resolver, if any).
* Future calls to g_resolver_get_default() will return this resolver.
*
* This can be used if an application wants to perform any sort of DNS
* caching or "pinning"; it can implement its own #GResolver that
* calls the original default resolver for DNS operations, and
* implements its own cache policies on top of that, and then set
* itself as the default resolver for all later code to use.
*
* Since: 2.22
**/
void
g_resolver_set_default (GResolver *resolver)
{
if (default_resolver)
g_object_unref (default_resolver);
default_resolver = g_object_ref (resolver);
}
/**
* g_resolver_lookup_by_name:
* @resolver: a #GResolver
* @hostname: the hostname to look up
* @cancellable: a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Synchronously resolves @hostname to determine its associated IP
* address(es). @hostname may be an ASCII-only or UTF-8 hostname, or
* the textual form of an IP address (in which case this just becomes
* a wrapper around g_inet_address_new_from_string()).
*
* On success, g_resolver_lookup_by_name() will return a #GList of
* #GInetAddress, sorted in order of preference. (That is, you should
* attempt to connect to the first address first, then the second if
* the first fails, etc.)
*
* If the DNS resolution fails, @error (if non-%NULL) will be set to a
* value from #GResolverError.
*
* If @cancellable is non-%NULL, it can be used to cancel the
* operation, in which case @error (if non-%NULL) will be set to
* %G_IO_ERROR_CANCELLED.
*
* Return value: a #GList of #GInetAddress, or %NULL on error. You
* must unref each of the addresses and free the list when you are
* done with it. (You can use g_resolver_free_addresses() to do this.)
*
* Since: 2.22
**/
GList *
g_resolver_lookup_by_name (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GError **error)
{
GInetAddress *addr;
GList *addrs;
gchar *ascii_hostname = NULL;
g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
g_return_val_if_fail (hostname != NULL, NULL);
/* Check if @hostname is just an IP address */
addr = g_inet_address_new_from_string (hostname);
if (addr)
return g_list_append (NULL, addr);
if (g_hostname_is_non_ascii (hostname))
hostname = ascii_hostname = g_hostname_to_ascii (hostname);
addrs = G_RESOLVER_GET_CLASS (resolver)->
lookup_by_name (resolver, hostname, cancellable, error);
g_free (ascii_hostname);
return addrs;
}
/**
* g_resolver_lookup_by_name_async:
* @resolver: a #GResolver
* @hostname: the hostname to look up the address of
* @cancellable: a #GCancellable, or %NULL
* @callback: callback to call after resolution completes
* @user_data: data for @callback
*
* Begins asynchronously resolving @hostname to determine its
* associated IP address(es), and eventually calls @callback, which
* must call g_resolver_lookup_by_name_finish() to get the result. See
* g_resolver_lookup_by_name() for more details.
*
* Since: 2.22
**/
void
g_resolver_lookup_by_name_async (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GInetAddress *addr;
gchar *ascii_hostname = NULL;
g_return_if_fail (G_IS_RESOLVER (resolver));
g_return_if_fail (hostname != NULL);
/* Check if @hostname is just an IP address */
addr = g_inet_address_new_from_string (hostname);
if (addr)
{
GSimpleAsyncResult *simple;
GList *addrs;
simple = g_simple_async_result_new (G_OBJECT (resolver),
callback, user_data,
g_resolver_lookup_by_name_async);
addrs = g_list_append (NULL, addr);
g_simple_async_result_set_op_res_gpointer (simple, addrs, (GDestroyNotify)g_resolver_free_addresses);
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
return;
}
if (g_hostname_is_non_ascii (hostname))
hostname = ascii_hostname = g_hostname_to_ascii (hostname);
G_RESOLVER_GET_CLASS (resolver)->
lookup_by_name_async (resolver, hostname, cancellable, callback, user_data);
g_free (ascii_hostname);
}
/**
* g_resolver_lookup_by_name_finish:
* @resolver: a #GResolver
* @result: the result passed to your #GAsyncReadyCallback
* @error: return location for a #GError, or %NULL
*
* Retrieves the result of a call to
* g_resolver_lookup_by_name_async().
*
* If the DNS resolution failed, @error (if non-%NULL) will be set to
* a value from #GResolverError. If the operation was cancelled,
* @error will be set to %G_IO_ERROR_CANCELLED.
*
* Return value: a #GList of #GInetAddress, or %NULL on error. See
* g_resolver_lookup_by_name() for more details.
*
* Since: 2.22
**/
GList *
g_resolver_lookup_by_name_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
if (G_IS_SIMPLE_ASYNC_RESULT (result))
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
if (g_simple_async_result_propagate_error (simple, error))
return NULL;
/* Handle the stringified-IP-addr case */
if (g_simple_async_result_get_source_tag (simple) == g_resolver_lookup_by_name_async)
{
GList *addrs;
addrs = g_simple_async_result_get_op_res_gpointer (simple);
g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
return addrs;
}
}
return G_RESOLVER_GET_CLASS (resolver)->
lookup_by_name_finish (resolver, result, error);
}
/**
* g_resolver_free_addresses:
* @addresses: a #GList of #GInetAddress
*
* Frees @addresses (which should be the return value from
* g_resolver_lookup_by_name() or g_resolver_lookup_by_name_finish()).
* (This is a convenience method; you can also simply free the results
* by hand.)
*
* Since: 2.22
**/
void
g_resolver_free_addresses (GList *addresses)
{
GList *a;
for (a = addresses; a; a = a->next)
g_object_unref (a->data);
g_list_free (addresses);
}
/**
* g_resolver_lookup_by_address:
* @resolver: a #GResolver
* @address: the address to reverse-resolve
* @cancellable: a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Synchronously reverse-resolves @address to determine its
* associated hostname.
*
* If the DNS resolution fails, @error (if non-%NULL) will be set to
* a value from #GResolverError.
*
* If @cancellable is non-%NULL, it can be used to cancel the
* operation, in which case @error (if non-%NULL) will be set to
* %G_IO_ERROR_CANCELLED.
*
* Return value: a hostname (either ASCII-only, or in ASCII-encoded
* form), or %NULL on error.
*
* Since: 2.22
**/
gchar *
g_resolver_lookup_by_address (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
g_return_val_if_fail (G_IS_INET_ADDRESS (address), NULL);
return G_RESOLVER_GET_CLASS (resolver)->
lookup_by_address (resolver, address, cancellable, error);
}
/**
* g_resolver_lookup_by_address_async:
* @resolver: a #GResolver
* @address: the address to reverse-resolve
* @cancellable: a #GCancellable, or %NULL
* @callback: callback to call after resolution completes
* @user_data: data for @callback
*
* Begins asynchronously reverse-resolving @address to determine its
* associated hostname, and eventually calls @callback, which must
* call g_resolver_lookup_by_address_finish() to get the final result.
*
* Since: 2.22
**/
void
g_resolver_lookup_by_address_async (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (G_IS_RESOLVER (resolver));
g_return_if_fail (G_IS_INET_ADDRESS (address));
G_RESOLVER_GET_CLASS (resolver)->
lookup_by_address_async (resolver, address, cancellable, callback, user_data);
}
/**
* g_resolver_lookup_by_address_finish:
* @resolver: a #GResolver
* @result: the result passed to your #GAsyncReadyCallback
* @error: return location for a #GError, or %NULL
*
* Retrieves the result of a previous call to
* g_resolver_lookup_by_address_async().
*
* If the DNS resolution failed, @error (if non-%NULL) will be set to
* a value from #GResolverError. If the operation was cancelled,
* @error will be set to %G_IO_ERROR_CANCELLED.
*
* Return value: a hostname (either ASCII-only, or in ASCII-encoded
* form), or %NULL on error.
*
* Since: 2.22
**/
gchar *
g_resolver_lookup_by_address_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
if (G_IS_SIMPLE_ASYNC_RESULT (result))
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
if (g_simple_async_result_propagate_error (simple, error))
return NULL;
}
return G_RESOLVER_GET_CLASS (resolver)->
lookup_by_address_finish (resolver, result, error);
}
static gchar *
g_resolver_get_service_rrname (const char *service,
const char *protocol,
const char *domain)
{
gchar *rrname, *ascii_domain = NULL;
if (g_hostname_is_non_ascii (domain))
domain = ascii_domain = g_hostname_to_ascii (domain);
rrname = g_strdup_printf ("_%s._%s.%s", service, protocol, domain);
g_free (ascii_domain);
return rrname;
}
/**
* g_resolver_lookup_service:
* @resolver: a #GResolver
* @service: the service type to look up (eg, "ldap")
* @protocol: the networking protocol to use for @service (eg, "tcp")
* @domain: the DNS domain to look up the service in
* @cancellable: a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Synchronously performs a DNS SRV lookup for the given @service and
* @protocol in the given @domain and returns an array of #GSrvTarget.
* @domain may be an ASCII-only or UTF-8 hostname. Note also that the
* @service and @protocol arguments <emphasis>do not</emphasis>
* include the leading underscore that appears in the actual DNS
* entry.
*
* On success, g_resolver_lookup_service() will return a #GList of
* #GSrvTarget, sorted in order of preference. (That is, you should
* attempt to connect to the first target first, then the second if
* the first fails, etc.)
*
* If the DNS resolution fails, @error (if non-%NULL) will be set to
* a value from #GResolverError.
*
* If @cancellable is non-%NULL, it can be used to cancel the
* operation, in which case @error (if non-%NULL) will be set to
* %G_IO_ERROR_CANCELLED.
*
* Return value: a #GList of #GSrvTarget, or %NULL on error. You must
* free each of the targets and the list when you are done with it.
* (You can use g_resolver_free_targets() to do this.)
*
* Since: 2.22
**/
GList *
g_resolver_lookup_service (GResolver *resolver,
const gchar *service,
const gchar *protocol,
const gchar *domain,
GCancellable *cancellable,
GError **error)
{
GList *targets;
gchar *rrname;
g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
g_return_val_if_fail (service != NULL, NULL);
g_return_val_if_fail (protocol != NULL, NULL);
g_return_val_if_fail (domain != NULL, NULL);
rrname = g_resolver_get_service_rrname (service, protocol, domain);
targets = G_RESOLVER_GET_CLASS (resolver)->
lookup_service (resolver, rrname, cancellable, error);
g_free (rrname);
return targets;
}
/**
* g_resolver_lookup_service_async:
* @resolver: a #GResolver
* @service: the service type to look up (eg, "ldap")
* @protocol: the networking protocol to use for @service (eg, "tcp")
* @domain: the DNS domain to look up the service in
* @cancellable: a #GCancellable, or %NULL
* @callback: callback to call after resolution completes
* @user_data: data for @callback
*
* Begins asynchronously performing a DNS SRV lookup for the given
* @service and @protocol in the given @domain, and eventually calls
* @callback, which must call g_resolver_lookup_service_finish() to
* get the final result. See g_resolver_lookup_service() for more
* details.
*
* Since: 2.22
**/
void
g_resolver_lookup_service_async (GResolver *resolver,
const gchar *service,
const gchar *protocol,
const gchar *domain,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
gchar *rrname;
g_return_if_fail (G_IS_RESOLVER (resolver));
g_return_if_fail (service != NULL);
g_return_if_fail (protocol != NULL);
g_return_if_fail (domain != NULL);
rrname = g_resolver_get_service_rrname (service, protocol, domain);
G_RESOLVER_GET_CLASS (resolver)->
lookup_service_async (resolver, rrname, cancellable, callback, user_data);
g_free (rrname);
}
/**
* g_resolver_lookup_service_finish:
* @resolver: a #GResolver
* @result: the result passed to your #GAsyncReadyCallback
* @error: return location for a #GError, or %NULL
*
* Retrieves the result of a previous call to
* g_resolver_lookup_service_async().
*
* If the DNS resolution failed, @error (if non-%NULL) will be set to
* a value from #GResolverError. If the operation was cancelled,
* @error will be set to %G_IO_ERROR_CANCELLED.
*
* Return value: a #GList of #GSrvTarget, or %NULL on error. See
* g_resolver_lookup_service() for more details.
*
* Since: 2.22
**/
GList *
g_resolver_lookup_service_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
if (G_IS_SIMPLE_ASYNC_RESULT (result))
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
if (g_simple_async_result_propagate_error (simple, error))
return NULL;
}
return G_RESOLVER_GET_CLASS (resolver)->
lookup_service_finish (resolver, result, error);
}
/**
* g_resolver_free_targets:
* @targets: a #GList of #GSrvTarget
*
* Frees @targets (which should be the return value from
* g_resolver_lookup_service() or g_resolver_lookup_service_finish()).
* (This is a convenience method; you can also simply free the
* results by hand.)
*
* Since: 2.22
**/
void
g_resolver_free_targets (GList *targets)
{
GList *t;
for (t = targets; t; t = t->next)
g_srv_target_free (t->data);
g_list_free (targets);
}
/**
* g_resolver_error_quark:
*
* Gets the #GResolver Error Quark.
*
* Return value: a #GQuark.
*
* Since: 2.22
**/
GQuark
g_resolver_error_quark (void)
{
return g_quark_from_static_string ("g-resolver-error-quark");
}
static GResolverError
g_resolver_error_from_addrinfo_error (gint err)
{
switch (err)
{
case EAI_FAIL:
case EAI_NODATA:
case EAI_NONAME:
return G_RESOLVER_ERROR_NOT_FOUND;
case EAI_AGAIN:
return G_RESOLVER_ERROR_TEMPORARY_FAILURE;
default:
return G_RESOLVER_ERROR_INTERNAL;
}
}
struct addrinfo _g_resolver_addrinfo_hints;
/* Private method to process a getaddrinfo() response. */
GList *
_g_resolver_addresses_from_addrinfo (const char *hostname,
struct addrinfo *res,
gint gai_retval,
GError **error)
{
struct addrinfo *ai;
GSocketAddress *sockaddr;
GInetAddress *addr;
GList *addrs;
if (gai_retval != 0)
{
g_set_error (error, G_RESOLVER_ERROR,
g_resolver_error_from_addrinfo_error (gai_retval),
_("Error resolving '%s': %s"),
hostname, gai_strerror (gai_retval));
return NULL;
}
g_return_val_if_fail (res != NULL, NULL);
addrs = NULL;
for (ai = res; ai; ai = ai->ai_next)
{
sockaddr = g_socket_address_new_from_native (ai->ai_addr, ai->ai_addrlen);
if (!sockaddr || !G_IS_INET_SOCKET_ADDRESS (sockaddr))
continue;
addr = g_object_ref (g_inet_socket_address_get_address ((GInetSocketAddress *)sockaddr));
addrs = g_list_prepend (addrs, addr);
g_object_unref (sockaddr);
}
return g_list_reverse (addrs);
}
/* Private method to set up a getnameinfo() request */
void
_g_resolver_address_to_sockaddr (GInetAddress *address,
struct sockaddr_storage *sa,
gsize *sa_len)
{
GSocketAddress *sockaddr;
sockaddr = g_inet_socket_address_new (address, 0);
g_socket_address_to_native (sockaddr, (struct sockaddr *)sa, sizeof (*sa));
*sa_len = g_socket_address_get_native_size (sockaddr);
g_object_unref (sockaddr);
}
/* Private method to process a getnameinfo() response. */
char *
_g_resolver_name_from_nameinfo (GInetAddress *address,
const gchar *name,
gint gni_retval,
GError **error)
{
if (gni_retval != 0)
{
gchar *phys;
phys = g_inet_address_to_string (address);
g_set_error (error, G_RESOLVER_ERROR,
g_resolver_error_from_addrinfo_error (gni_retval),
_("Error reverse-resolving '%s': %s"),
phys ? phys : "(unknown)", gai_strerror (gni_retval));
g_free (phys);
return NULL;
}
return g_strdup (name);
}
#if defined(G_OS_UNIX)
/* Private method to process a res_query response into GSrvTargets */
GList *
_g_resolver_targets_from_res_query (const gchar *rrname,
guchar *answer,
gint len,
gint herr,
GError **error)
{
gint count;
gchar namebuf[1024];
guchar *end, *p;
guint16 type, qclass, rdlength, priority, weight, port;
guint32 ttl;
HEADER *header;
GSrvTarget *target;
GList *targets;
if (len <= 0)
{
GResolverError errnum;
const gchar *format;
if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
{
errnum = G_RESOLVER_ERROR_NOT_FOUND;
format = _("No service record for '%s'");
}
else if (herr == TRY_AGAIN)
{
errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE;
format = _("Temporarily unable to resolve '%s'");
}
else
{
errnum = G_RESOLVER_ERROR_INTERNAL;
format = _("Error resolving '%s'");
}
g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname);
return NULL;
}
targets = NULL;
header = (HEADER *)answer;
p = answer + sizeof (HEADER);
end = answer + len;
/* Skip query */
count = ntohs (header->qdcount);
while (count-- && p < end)
{
p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
p += 4;
}
/* Read answers */
count = ntohs (header->ancount);
while (count-- && p < end)
{
p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
GETSHORT (type, p);
GETSHORT (qclass, p);
GETLONG (ttl, p);
GETSHORT (rdlength, p);
if (type != T_SRV || qclass != C_IN)
{
p += rdlength;
continue;
}
GETSHORT (priority, p);
GETSHORT (weight, p);
GETSHORT (port, p);
p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
target = g_srv_target_new (namebuf, port, priority, weight);
targets = g_list_prepend (targets, target);
}
return g_srv_target_list_sort (targets);
}
#elif defined(G_OS_WIN32)
/* Private method to process a DnsQuery response into GSrvTargets */
GList *
_g_resolver_targets_from_DnsQuery (const gchar *rrname,
DNS_STATUS status,
DNS_RECORD *results,
GError **error)
{
DNS_RECORD *rec;
GSrvTarget *target;
GList *targets;
if (status != ERROR_SUCCESS)
{
GResolverError errnum;
const gchar *format;
if (status == DNS_ERROR_RCODE_NAME_ERROR)
{
errnum = G_RESOLVER_ERROR_NOT_FOUND;
format = _("No service record for '%s'");
}
else if (status == DNS_ERROR_RCODE_SERVER_FAILURE)
{
errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE;
format = _("Temporarily unable to resolve '%s'");
}
else
{
errnum = G_RESOLVER_ERROR_INTERNAL;
format = _("Error resolving '%s'");
}
g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname);
return NULL;
}
targets = NULL;
for (rec = results; rec; rec = rec->pNext)
{
if (rec->wType != DNS_TYPE_SRV)
continue;
target = g_srv_target_new (rec->Data.SRV.pNameTarget,
rec->Data.SRV.wPort,
rec->Data.SRV.wPriority,
rec->Data.SRV.wWeight);
targets = g_list_prepend (targets, target);
}
return g_srv_target_list_sort (targets);
}
#endif
#define __G_RESOLVER_C__
#include "gioaliasdef.c"

159
gio/gresolver.h Normal file
View File

@ -0,0 +1,159 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#ifndef __G_RESOLVER_H__
#define __G_RESOLVER_H__
#include <gio/giotypes.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, GResolverClass))
struct _GResolver {
GObject parent_instance;
};
typedef struct {
GObjectClass parent_class;
GList * ( *lookup_by_name) (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GError **error);
void ( *lookup_by_name_async) (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GList * ( *lookup_by_name_finish) (GResolver *resolver,
GAsyncResult *result,
GError **error);
gchar * ( *lookup_by_address) (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GError **error);
void ( *lookup_by_address_async) (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gchar * ( *lookup_by_address_finish) (GResolver *resolver,
GAsyncResult *result,
GError **error);
GList * ( *lookup_service) (GResolver *resolver,
const gchar *rrname,
GCancellable *cancellable,
GError **error);
void ( *lookup_service_async) (GResolver *resolver,
const gchar *rrname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GList * ( *lookup_service_finish) (GResolver *resolver,
GAsyncResult *result,
GError **error);
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
} GResolverClass;
GType g_resolver_get_type (void) G_GNUC_CONST;
GResolver *g_resolver_get_default (void);
void g_resolver_set_default (GResolver *resolver);
GList *g_resolver_lookup_by_name (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GError **error);
void g_resolver_lookup_by_name_async (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GList *g_resolver_lookup_by_name_finish (GResolver *resolver,
GAsyncResult *result,
GError **error);
void g_resolver_free_addresses (GList *addresses);
gchar *g_resolver_lookup_by_address (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GError **error);
void g_resolver_lookup_by_address_async (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gchar *g_resolver_lookup_by_address_finish (GResolver *resolver,
GAsyncResult *result,
GError **error);
GList *g_resolver_lookup_service (GResolver *resolver,
const gchar *service,
const gchar *protocol,
const gchar *domain,
GCancellable *cancellable,
GError **error);
void g_resolver_lookup_service_async (GResolver *resolver,
const gchar *service,
const gchar *protocol,
const gchar *domain,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GList *g_resolver_lookup_service_finish (GResolver *resolver,
GAsyncResult *result,
GError **error);
void g_resolver_free_targets (GList *targets);
/**
* G_RESOLVER_ERROR:
*
* Error domain for #GResolver. Errors in this domain will be from the
* #GResolverError enumeration. See #GError for more information on
* error domains.
**/
#define G_RESOLVER_ERROR (g_resolver_error_quark ())
GQuark g_resolver_error_quark (void);
G_END_DECLS
#endif /* __G_RESOLVER_H__ */

334
gio/gsrvtarget.c Normal file
View File

@ -0,0 +1,334 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#include "config.h"
#include <glib.h>
#include "glibintl.h"
#include "gsrvtarget.h"
#include <stdlib.h>
#include <string.h>
#include "gioalias.h"
/**
* SECTION:gsrvtarget
* @short_description: DNS SRV record target
* @include: gio/gio.h
*
* SRV (service) records are used by some network protocols to provide
* service-specific aliasing and load-balancing. For example, XMPP
* (Jabber) uses SRV records to locate the XMPP server for a domain;
* rather than connecting directly to "example.com" or assuming a
* specific server hostname like "xmpp.example.com", an XMPP client
* would look up the "xmpp-client" SRV record for "example.com", and
* then connect to whatever host was pointed to by that record.
*
* Use g_resolver_lookup_service() or
* g_resolver_lookup_service_async() to find the #GSrvTarget<!-- -->s
* for a given service.
**/
struct _GSrvTarget {
gchar *hostname;
guint16 port;
guint16 priority;
guint16 weight;
};
/**
* GSrvTarget:
*
* A single target host/port that a network service is running on.
*/
GType
g_srv_target_get_type (void)
{
static volatile gsize type_volatile = 0;
if (g_once_init_enter (&type_volatile))
{
GType type = g_boxed_type_register_static (
g_intern_static_string ("GSrvTarget"),
(GBoxedCopyFunc) g_srv_target_copy,
(GBoxedFreeFunc) g_srv_target_free);
g_once_init_leave (&type_volatile, type);
}
return type_volatile;
}
/**
* g_srv_target_new:
* @hostname: the host that the service is running on
* @port: the port that the service is running on
* @priority: the target's priority
* @weight: the target's weight
*
* Creates a new #GSrvTarget with the given parameters.
*
* You should not need to use this; normally #GSrvTarget<!-- -->s are
* created by #GResolver.
*
* Return value: a new #GSrvTarget.
*
* Since: 2.22
**/
GSrvTarget *
g_srv_target_new (const gchar *hostname,
guint16 port,
guint16 priority,
guint16 weight)
{
GSrvTarget *target = g_slice_new0 (GSrvTarget);
target->hostname = g_strdup (hostname);
target->port = port;
target->priority = priority;
target->weight = weight;
return target;
}
/**
* g_srv_target_copy:
* @target: a #GSrvTarget
*
* Copies @target
*
* Return value: a copy of @target
*
* Since: 2.22
**/
GSrvTarget *
g_srv_target_copy (GSrvTarget *target)
{
return g_srv_target_new (target->hostname, target->port,
target->priority, target->weight);
}
/**
* g_srv_target_free:
* @target: a #GSrvTarget
*
* Frees @target
*
* Since: 2.22
**/
void
g_srv_target_free (GSrvTarget *target)
{
g_free (target->hostname);
g_slice_free (GSrvTarget, target);
}
/**
* g_srv_target_get_hostname:
* @target: a #GSrvTarget
*
* Gets @target's hostname (in ASCII form; if you are going to present
* this to the user, you should use g_hostname_is_ascii_encoded() to
* check if it contains encoded Unicode segments, and use
* g_hostname_to_unicode() to convert it if it does.)
*
* Return value: @target's hostname
*
* Since: 2.22
**/
const gchar *
g_srv_target_get_hostname (GSrvTarget *target)
{
return target->hostname;
}
/**
* g_srv_target_get_port:
* @target: a #GSrvTarget
*
* Gets @target's port
*
* Return value: @target's port
*
* Since: 2.22
**/
guint16
g_srv_target_get_port (GSrvTarget *target)
{
return target->port;
}
/**
* g_srv_target_get_priority:
* @target: a #GSrvTarget
*
* Gets @target's priority. You should not need to look at this;
* #GResolver already sorts the targets according to the algorithm in
* RFC 2782.
*
* Return value: @target's priority
*
* Since: 2.22
**/
guint16
g_srv_target_get_priority (GSrvTarget *target)
{
return target->priority;
}
/**
* g_srv_target_get_weight:
* @target: a #GSrvTarget
*
* Gets @target's weight. You should not need to look at this;
* #GResolver already sorts the targets according to the algorithm in
* RFC 2782.
*
* Return value: @target's weight
*
* Since: 2.22
**/
guint16
g_srv_target_get_weight (GSrvTarget *target)
{
return target->weight;
}
gint
compare_target (gconstpointer a, gconstpointer b)
{
GSrvTarget *ta = (GSrvTarget *)a;
GSrvTarget *tb = (GSrvTarget *)b;
if (ta->priority == tb->priority)
{
/* Arrange targets of the same priority "in any order, except
* that all those with weight 0 are placed at the beginning of
* the list"
*/
if (ta->weight == 0)
return -1;
else if (tb->weight == 0)
return 1;
else
return g_random_int_range (-1, 1);
}
else
return ta->priority - tb->priority;
}
/**
* g_srv_target_list_sort:
* @targets: a #GList of #GSrvTarget
*
* Sorts @targets in place according to the algorithm in RFC 2782.
*
* Return value: the head of the sorted list.
*
* Since: 2.22
**/
GList *
g_srv_target_list_sort (GList *targets)
{
gint sum, val, priority, weight;
GList *first, *last, *n;
GSrvTarget *target;
gpointer tmp;
if (!targets)
return NULL;
if (!targets->next)
{
target = targets->data;
if (!strcmp (target->hostname, "."))
{
/* 'A Target of "." means that the service is decidedly not
* available at this domain.'
*/
g_srv_target_free (target);
g_list_free (targets);
return NULL;
}
}
/* Sort by priority, and partly by weight */
targets = g_list_sort (targets, compare_target);
/* For each group of targets with the same priority, rebalance them
* according to weight.
*/
for (first = targets; first; first = last->next)
{
/* Skip @first to a non-0-weight target. */
while (first && ((GSrvTarget *)first->data)->weight == 0)
first = first->next;
if (!first)
break;
/* Skip @last to the last target of the same priority. */
priority = ((GSrvTarget *)first->data)->priority;
last = first;
while (last->next &&
((GSrvTarget *)last->next->data)->priority == priority)
last = last->next;
/* If there's only one non-0 weight target at this priority,
* we can move on to the next priority level.
*/
if (last == first)
continue;
/* Randomly reorder the non-0 weight targets, giving precedence
* to the ones with higher weight. RFC 2782 describes this in
* terms of assigning a running sum to each target and building
* a new list. We do things slightly differently, but should get
* the same result.
*/
for (n = first, sum = 0; n != last->next; n = n->next)
sum += ((GSrvTarget *)n->data)->weight;
while (first != last)
{
val = g_random_int_range (0, sum);
for (n = first; n != last; n = n->next)
{
weight = ((GSrvTarget *)n->data)->weight;
if (val < weight)
break;
val -= weight;
}
tmp = first->data;
first->data = n->data;
n->data = tmp;
sum -= weight;
first = first->next;
}
}
return targets;
}
#define __G_SRV_TARGET_C__
#include "gioaliasdef.c"

52
gio/gsrvtarget.h Normal file
View File

@ -0,0 +1,52 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#ifndef __G_SRV_TARGET_H__
#define __G_SRV_TARGET_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
GType g_srv_target_get_type (void) G_GNUC_CONST;
#define G_TYPE_SRV_TARGET (g_srv_target_get_type ())
GSrvTarget *g_srv_target_new (const gchar *hostname,
guint16 port,
guint16 priority,
guint16 weight);
GSrvTarget *g_srv_target_copy (GSrvTarget *target);
void g_srv_target_free (GSrvTarget *target);
const gchar *g_srv_target_get_hostname (GSrvTarget *target);
guint16 g_srv_target_get_port (GSrvTarget *target);
guint16 g_srv_target_get_priority (GSrvTarget *target);
guint16 g_srv_target_get_weight (GSrvTarget *target);
GList *g_srv_target_list_sort (GList *targets);
G_END_DECLS
#endif /* __G_SRV_TARGET_H__ */

617
gio/gthreadedresolver.c Normal file
View File

@ -0,0 +1,617 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#include "config.h"
#include <glib.h>
#include "glibintl.h"
#include <stdio.h>
#include <string.h>
#include "gthreadedresolver.h"
#include "gnetworkingprivate.h"
#include "gcancellable.h"
#include "gsimpleasyncresult.h"
#include "gsocketaddress.h"
#include "gioalias.h"
G_DEFINE_TYPE (GThreadedResolver, g_threaded_resolver, G_TYPE_RESOLVER)
static void threaded_resolver_thread (gpointer thread_data, gpointer pool_data);
static void
g_threaded_resolver_init (GThreadedResolver *gtr)
{
if (g_thread_supported ())
{
gtr->thread_pool = g_thread_pool_new (threaded_resolver_thread, gtr,
-1, FALSE, NULL);
}
}
static void
finalize (GObject *object)
{
GThreadedResolver *gtr = G_THREADED_RESOLVER (object);
g_thread_pool_free (gtr->thread_pool, FALSE, FALSE);
G_OBJECT_CLASS (g_threaded_resolver_parent_class)->finalize (object);
}
/* A GThreadedResolverRequest represents a request in progress
* (usually, but see case 1). It is refcounted, to make sure that it
* doesn't get freed too soon. In particular, it can't be freed until
* (a) the resolver thread has finished resolving, (b) the calling
* thread has received an answer, and (c) no other thread could be in
* the process of trying to cancel it.
*
* The possibilities:
*
* 1. Synchronous non-cancellable request: in this case, the request
* is simply done in the calling thread, without using
* GThreadedResolverRequest at all.
*
* 2. Synchronous cancellable request: A req is created with a GCond,
* and 3 refs (for the resolution thread, the calling thread, and
* the cancellation signal handler).
*
* a. If the resolution completes successfully, the thread pool
* function (threaded_resolver_thread()) will call
* g_threaded_resolver_request_complete(), which will detach
* the "cancelled" signal handler (dropping one ref on req)
* and signal the GCond, and then unref the req. The calling
* thread receives the signal from the GCond, processes the
* response, and unrefs the req, causing it to be freed.
*
* b. If the resolution is cancelled before completing,
* request_cancelled() will call
* g_threaded_resolver_request_complete(), which will detach
* the signal handler (as above, unreffing the req), set
* req->error to indicate that it was cancelled, and signal
* the GCond. The calling thread receives the signal from the
* GCond, processes the response, and unrefs the req.
* Eventually, the resolver thread finishes resolving (or
* times out in the resolver) and calls
* g_threaded_resolver_request_complete() again, but
* _request_complete() does nothing this time since the
* request is already complete. The thread pool func then
* unrefs the req, causing it to be freed.
*
* 3. Asynchronous request: A req is created with a GSimpleAsyncResult
* (and no GCond). The calling thread's ref on req is set up to be
* automatically dropped when the async_result is freed. Two
* sub-possibilities:
*
* a. If the resolution completes, the thread pool function
* (threaded_resolver_thread()) will call
* g_threaded_resolver_request_complete(), which will detach
* the "cancelled" signal handler (if it was present)
* (unreffing the req), queue the async_result to complete in
* an idle handler, unref the async_result (which is still
* reffed by the idle handler though), and then unref the req.
* The main thread then invokes the async_result's callback
* and processes the response. When it finishes, the
* async_result drops the ref that was taken by
* g_simple_async_result_complete_in_idle(), which causes the
* async_result to be freed, which causes req to be unreffed
* and freed.
*
* b. If the resolution is cancelled, request_cancelled() will
* call g_threaded_resolver_request_complete(), which will
* detach the signal handler (as above, unreffing the req) set
* req->error to indicate that it was cancelled, and queue and
* unref the async_result. The main thread completes the
* async_request and unrefs it and the req, as above.
* Eventually, the resolver thread finishes resolving (or
* times out in the resolver) and calls
* g_threaded_resolver_request_complete() again, but
* _request_complete() does nothing this time since the
* request is already complete. The thread pool func then
* unrefs the req, causing it to be freed.
*
* g_threaded_resolver_request_complete() ensures that if the request
* completes and cancels "at the same time" that only one of the two
* conditions gets processed.
*/
typedef struct _GThreadedResolverRequest GThreadedResolverRequest;
typedef void (*GThreadedResolverResolveFunc) (GThreadedResolverRequest *, GError **);
typedef void (*GThreadedResolverFreeFunc) (GThreadedResolverRequest *);
struct _GThreadedResolverRequest {
GThreadedResolverResolveFunc resolve_func;
GThreadedResolverFreeFunc free_func;
union {
struct {
gchar *hostname;
GList *addresses;
} name;
struct {
GInetAddress *address;
gchar *name;
} address;
struct {
gchar *rrname;
GList *targets;
} service;
} u;
GCancellable *cancellable;
GError *error;
GMutex *mutex;
guint ref_count;
GCond *cond;
GSimpleAsyncResult *async_result;
gboolean complete;
};
static void g_threaded_resolver_request_unref (GThreadedResolverRequest *req);
static void request_cancelled (GCancellable *cancellable, gpointer req);
static void request_cancelled_disconnect_notify (gpointer req, GClosure *closure);
static GThreadedResolverRequest *
g_threaded_resolver_request_new (GThreadedResolverResolveFunc resolve_func,
GThreadedResolverFreeFunc free_func,
GCancellable *cancellable)
{
GThreadedResolverRequest *req;
req = g_slice_new0 (GThreadedResolverRequest);
req->resolve_func = resolve_func;
req->free_func = free_func;
/* Initial refcount is 2; one for the caller and one for resolve_func */
req->ref_count = 2;
if (g_thread_supported ())
req->mutex = g_mutex_new ();
/* Initially locked; caller must unlock */
g_mutex_lock (req->mutex);
if (cancellable)
{
req->ref_count++;
req->cancellable = g_object_ref (cancellable);
g_signal_connect_data (cancellable, "cancelled",
G_CALLBACK (request_cancelled), req,
request_cancelled_disconnect_notify, 0);
}
return req;
}
static void
g_threaded_resolver_request_unref (GThreadedResolverRequest *req)
{
guint ref_count;
g_mutex_lock (req->mutex);
ref_count = --req->ref_count;
g_mutex_unlock (req->mutex);
if (ref_count > 0)
return;
g_mutex_free (req->mutex);
if (req->cond)
g_cond_free (req->cond);
if (req->error)
g_error_free (req->error);
if (req->free_func)
req->free_func (req);
/* We don't have to free req->cancellable or req->async_result,
* since (if set), they must already have been freed by
* request_complete() in order to get here.
*/
g_slice_free (GThreadedResolverRequest, req);
}
static void
g_threaded_resolver_request_complete (GThreadedResolverRequest *req,
gboolean cancelled)
{
g_mutex_lock (req->mutex);
if (req->complete)
{
/* The req was cancelled, and now it has finished resolving as
* well. But we have nowhere to send the result, so just return.
*/
g_mutex_unlock (req->mutex);
return;
}
req->complete = TRUE;
g_mutex_unlock (req->mutex);
if (req->cancellable)
{
/* Possibly propagate a cancellation error */
if (cancelled && !req->error)
g_cancellable_set_error_if_cancelled (req->cancellable, &req->error);
/* Drop the signal handler's ref on @req */
g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
g_object_unref (req->cancellable);
req->cancellable = NULL;
}
if (req->cond)
g_cond_signal (req->cond);
else if (req->async_result)
{
if (req->error)
g_simple_async_result_set_from_error (req->async_result, req->error);
g_simple_async_result_complete_in_idle (req->async_result);
/* Drop our ref on the async_result, which will eventually cause
* it to drop its ref on req.
*/
g_object_unref (req->async_result);
req->async_result = NULL;
}
}
static void
request_cancelled (GCancellable *cancellable,
gpointer user_data)
{
GThreadedResolverRequest *req = user_data;
g_threaded_resolver_request_complete (req, TRUE);
/* We can't actually cancel the resolver thread; it will eventually
* complete on its own and call request_complete() again, which will
* do nothing the second time.
*/
}
static void
request_cancelled_disconnect_notify (gpointer req,
GClosure *closure)
{
g_threaded_resolver_request_unref (req);
}
static void
threaded_resolver_thread (gpointer thread_data,
gpointer pool_data)
{
GThreadedResolverRequest *req = thread_data;
req->resolve_func (req, &req->error);
g_threaded_resolver_request_complete (req, FALSE);
g_threaded_resolver_request_unref (req);
}
static void
resolve_sync (GThreadedResolver *gtr,
GThreadedResolverRequest *req,
GError **error)
{
if (!req->cancellable || !gtr->thread_pool)
{
req->resolve_func (req, error);
return;
}
req->cond = g_cond_new ();
g_thread_pool_push (gtr->thread_pool, req, NULL);
g_cond_wait (req->cond, req->mutex);
g_mutex_unlock (req->mutex);
if (req->error)
{
g_propagate_error (error, req->error);
req->error = NULL;
}
}
static void
resolve_async (GThreadedResolver *gtr,
GThreadedResolverRequest *req,
GAsyncReadyCallback callback,
gpointer user_data,
gpointer tag)
{
req->async_result = g_simple_async_result_new (G_OBJECT (gtr),
callback, user_data, tag);
g_simple_async_result_set_op_res_gpointer (req->async_result, req, NULL);
g_thread_pool_push (gtr->thread_pool, req, NULL);
g_mutex_unlock (req->mutex);
}
static GThreadedResolverRequest *
resolve_finish (GResolver *resolver,
GAsyncResult *result,
gpointer tag,
GError **error)
{
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), tag), NULL);
return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
}
static void
do_lookup_by_name (GThreadedResolverRequest *req,
GError **error)
{
struct addrinfo *res = NULL;
gint retval;
retval = getaddrinfo (req->u.name.hostname, NULL,
&_g_resolver_addrinfo_hints, &res);
req->u.name.addresses =
_g_resolver_addresses_from_addrinfo (req->u.name.hostname, res, retval, error);
if (res)
freeaddrinfo (res);
}
static GList *
lookup_by_name (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GError **error)
{
GThreadedResolver *gtr = G_THREADED_RESOLVER (resolver);
GThreadedResolverRequest *req;
GList *addresses;
req = g_threaded_resolver_request_new (do_lookup_by_name, NULL, cancellable);
req->u.name.hostname = (gchar *)hostname;
resolve_sync (gtr, req, error);
addresses = req->u.name.addresses;
g_threaded_resolver_request_unref (req);
return addresses;
}
static void
free_lookup_by_name (GThreadedResolverRequest *req)
{
g_free (req->u.name.hostname);
if (req->u.name.addresses)
g_resolver_free_addresses (req->u.name.addresses);
}
static void
lookup_by_name_async (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GThreadedResolver *gtr = G_THREADED_RESOLVER (resolver);
GThreadedResolverRequest *req;
req = g_threaded_resolver_request_new (do_lookup_by_name, free_lookup_by_name,
cancellable);
req->u.name.hostname = g_strdup (hostname);
resolve_async (gtr, req, callback, user_data, lookup_by_name_async);
}
static GList *
lookup_by_name_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
GThreadedResolverRequest *req;
GList *addresses;
req = resolve_finish (resolver, result, lookup_by_name_async, error);
addresses = req->u.name.addresses;
req->u.name.addresses = NULL;
return addresses;
}
static void
do_lookup_by_address (GThreadedResolverRequest *req,
GError **error)
{
struct sockaddr_storage sockaddr;
gsize sockaddr_size;
gchar name[NI_MAXHOST];
gint retval;
_g_resolver_address_to_sockaddr (req->u.address.address,
&sockaddr, &sockaddr_size);
retval = getnameinfo ((struct sockaddr *)&sockaddr, sockaddr_size,
name, sizeof (name), NULL, 0, NI_NAMEREQD);
req->u.address.name = _g_resolver_name_from_nameinfo (req->u.address.address,
name, retval, error);
}
static gchar *
lookup_by_address (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GError **error)
{
GThreadedResolver *gtr = G_THREADED_RESOLVER (resolver);
GThreadedResolverRequest *req;
gchar *name;
req = g_threaded_resolver_request_new (do_lookup_by_address, NULL, cancellable);
req->u.address.address = address;
resolve_sync (gtr, req, error);
name = req->u.address.name;
g_threaded_resolver_request_unref (req);
return name;
}
static void
free_lookup_by_address (GThreadedResolverRequest *req)
{
g_object_unref (req->u.address.address);
if (req->u.address.name)
g_free (req->u.address.name);
}
static void
lookup_by_address_async (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GThreadedResolver *gtr = G_THREADED_RESOLVER (resolver);
GThreadedResolverRequest *req;
req = g_threaded_resolver_request_new (do_lookup_by_address,
free_lookup_by_address,
cancellable);
req->u.address.address = g_object_ref (address);
resolve_async (gtr, req, callback, user_data, lookup_by_address_async);
}
static gchar *
lookup_by_address_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
GThreadedResolverRequest *req;
gchar *name;
req = resolve_finish (resolver, result, lookup_by_address_async, error);
name = req->u.address.name;
req->u.address.name = NULL;
return name;
}
static void
do_lookup_service (GThreadedResolverRequest *req,
GError **error)
{
#if defined(G_OS_UNIX)
gint len, herr;
guchar answer[1024];
#elif defined(G_OS_WIN32)
DNS_STATUS status;
DNS_RECORD *results;
#endif
#if defined(G_OS_UNIX)
len = res_query (req->u.service.rrname, C_IN, T_SRV, answer, sizeof (answer));
herr = h_errno;
req->u.service.targets = _g_resolver_targets_from_res_query (req->u.service.rrname, answer, len, herr, error);
#elif defined(G_OS_WIN32)
status = DnsQuery_A (req->u.service.rrname, DNS_TYPE_SRV,
DNS_QUERY_STANDARD, NULL, &results, NULL);
req->u.service.targets = _g_resolver_targets_from_DnsQuery (req->u.service.rrname, status, results, error);
DnsRecordListFree (results, DnsFreeRecordList);
#endif
}
static GList *
lookup_service (GResolver *resolver,
const gchar *rrname,
GCancellable *cancellable,
GError **error)
{
GThreadedResolver *gtr = G_THREADED_RESOLVER (resolver);
GThreadedResolverRequest *req;
GList *targets;
req = g_threaded_resolver_request_new (do_lookup_service, NULL, cancellable);
req->u.service.rrname = (char *)rrname;
resolve_sync (gtr, req, error);
targets = req->u.service.targets;
g_threaded_resolver_request_unref (req);
return targets;
}
static void
free_lookup_service (GThreadedResolverRequest *req)
{
g_free (req->u.service.rrname);
if (req->u.service.targets)
g_resolver_free_targets (req->u.service.targets);
}
static void
lookup_service_async (GResolver *resolver,
const char *rrname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GThreadedResolver *gtr = G_THREADED_RESOLVER (resolver);
GThreadedResolverRequest *req;
req = g_threaded_resolver_request_new (do_lookup_service,
free_lookup_service,
cancellable);
req->u.service.rrname = g_strdup (rrname);
resolve_async (gtr, req, callback, user_data, lookup_service_async);
}
static GList *
lookup_service_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
GThreadedResolverRequest *req;
GList *targets;
req = resolve_finish (resolver, result, lookup_service_async, error);
targets = req->u.service.targets;
req->u.service.targets = NULL;
return targets;
}
static void
g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
{
GResolverClass *resolver_class = G_RESOLVER_CLASS (threaded_class);
GObjectClass *object_class = G_OBJECT_CLASS (threaded_class);
resolver_class->lookup_by_name = lookup_by_name;
resolver_class->lookup_by_name_async = lookup_by_name_async;
resolver_class->lookup_by_name_finish = lookup_by_name_finish;
resolver_class->lookup_by_address = lookup_by_address;
resolver_class->lookup_by_address_async = lookup_by_address_async;
resolver_class->lookup_by_address_finish = lookup_by_address_finish;
resolver_class->lookup_service = lookup_service;
resolver_class->lookup_service_async = lookup_service_async;
resolver_class->lookup_service_finish = lookup_service_finish;
object_class->finalize = finalize;
}
#define __G_THREADED_RESOLVER_C__
#include "gioaliasdef.c"

50
gio/gthreadedresolver.h Normal file
View File

@ -0,0 +1,50 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#ifndef __G_THREADED_RESOLVER_H__
#define __G_THREADED_RESOLVER_H__
#include <gio/gresolver.h>
G_BEGIN_DECLS
#define G_TYPE_THREADED_RESOLVER (g_threaded_resolver_get_type ())
#define G_THREADED_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_THREADED_RESOLVER, GThreadedResolver))
#define G_THREADED_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_THREADED_RESOLVER, GThreadedResolverClass))
#define G_IS_THREADED_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_THREADED_RESOLVER))
#define G_IS_THREADED_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_THREADED_RESOLVER))
#define G_THREADED_RESOLVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_THREADED_RESOLVER, GThreadedResolverClass))
typedef struct {
GResolver parent_instance;
GThreadPool *thread_pool;
} GThreadedResolver;
typedef struct {
GResolverClass parent_class;
} GThreadedResolverClass;
GType g_threaded_resolver_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_RESOLVER_H__ */

433
gio/gunixresolver.c Normal file
View File

@ -0,0 +1,433 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#include "config.h"
#include <glib.h>
#include "glibintl.h"
#include <resolv.h>
#include <stdio.h>
#include <string.h>
#include "gunixresolver.h"
#include "gnetworkingprivate.h"
#include "gcancellable.h"
#include "gsimpleasyncresult.h"
#include "gsocketaddress.h"
#include "gioalias.h"
G_DEFINE_TYPE (GUnixResolver, g_unix_resolver, G_TYPE_THREADED_RESOLVER)
static gboolean g_unix_resolver_watch (GIOChannel *iochannel,
GIOCondition condition,
gpointer user_data);
static void
g_unix_resolver_init (GUnixResolver *gur)
{
gint fd;
GIOChannel *io;
/* FIXME: how many workers? */
gur->asyncns = _g_asyncns_new (2);
fd = _g_asyncns_fd (gur->asyncns);
io = g_io_channel_unix_new (fd);
gur->watch = g_io_add_watch (io, G_IO_IN | G_IO_HUP | G_IO_ERR,
g_unix_resolver_watch, gur);
g_io_channel_unref (io);
}
static void
g_unix_resolver_finalize (GObject *object)
{
GUnixResolver *gur = G_UNIX_RESOLVER (object);
if (gur->watch)
g_source_remove (gur->watch);
_g_asyncns_free (gur->asyncns);
G_OBJECT_CLASS (g_unix_resolver_parent_class)->finalize (object);
}
/* The various request possibilities:
*
* 1. Synchronous: handed off to the base class (GThreadedResolver);
* since it's never possible to cancel a synchronous request in a
* single-threaded program, the request is done in the calling
* thread.
*
* 2. Asynchronous: An appropriate _g_asyncns_query_t is created, and
* then a GUnixResolverRequest is created with that query and a
* GSimpleAsyncResult. Two sub-possibilities:
*
* a. The resolution completes: g_unix_resolver_watch() sees that
* the request has completed, and calls
* g_unix_resolver_request_complete(), which detaches the
* "cancelled" signal handler (if it was present) and then
* immediately completes the async_result (since
* g_unix_resolver_watch() is already run from main-loop
* time.) After completing the async_result, it unrefs it,
* causing the req to be freed as well.
*
* b. The resolution is cancelled: request_cancelled() calls
* _g_asyncns_cancel() to cancel the resolution. Then it calls
* g_unix_resolver_request_complete(), which detaches the
* signal handler, and queues async_result to complete in an
* idle handler. It then unrefs the async_result to ensure
* that after its callback runs, it will be destroyed, in turn
* causing the req to be freed. Because the asyncns resolution
* was cancelled, g_unix_resolver_watch() will never be
* triggered for this req.
*
* Since there's only a single thread, it's not possible for the
* request to both complete and be cancelled "at the same time",
* and each of the two possibilities takes steps to block the other
* from being able to happen later, so it's always safe to free req
* after the async_result completes.
*/
typedef struct _GUnixResolverRequest GUnixResolverRequest;
typedef void (*GUnixResolverFreeFunc) (GUnixResolverRequest *);
struct _GUnixResolverRequest {
GUnixResolver *gur;
_g_asyncns_query_t *qy;
union {
gchar *hostname;
GInetAddress *address;
gchar *service;
} u;
GUnixResolverFreeFunc free_func;
GCancellable *cancellable;
GSimpleAsyncResult *async_result;
};
static void g_unix_resolver_request_free (GUnixResolverRequest *req);
static void request_cancelled (GCancellable *cancellable,
gpointer user_data);
static GUnixResolverRequest *
g_unix_resolver_request_new (GUnixResolver *gur,
_g_asyncns_query_t *qy,
GUnixResolverFreeFunc free_func,
GCancellable *cancellable,
GSimpleAsyncResult *async_result)
{
GUnixResolverRequest *req;
req = g_slice_new0 (GUnixResolverRequest);
req->gur = g_object_ref (gur);
req->qy = qy;
req->free_func = free_func;
if (cancellable)
{
req->cancellable = g_object_ref (cancellable);
g_signal_connect (cancellable, "cancelled",
G_CALLBACK (request_cancelled), req);
}
req->async_result = g_object_ref (async_result);
g_simple_async_result_set_op_res_gpointer (req->async_result, req, (GDestroyNotify)g_unix_resolver_request_free);
return req;
}
static void
g_unix_resolver_request_free (GUnixResolverRequest *req)
{
/* If the user didn't call _finish the qy will still be around. */
if (req->qy)
_g_asyncns_cancel (req->gur->asyncns, req->qy);
/* We don't have to free req->cancellable and req->async_result,
* since they must already have been freed if we're here.
*/
g_slice_free (GUnixResolverRequest, req);
}
static void
g_unix_resolver_request_complete (GUnixResolverRequest *req,
gboolean need_idle)
{
if (req->cancellable)
{
g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
g_object_unref (req->cancellable);
req->cancellable = NULL;
}
if (need_idle)
g_simple_async_result_complete_in_idle (req->async_result);
else
g_simple_async_result_complete (req->async_result);
/* If we completed_in_idle, that will have taken an extra ref on
* req->async_result; if not, then we're already done. Either way we
* need to unref the async_result to make sure it eventually is
* destroyed, causing req to be freed.
*/
g_object_unref (req->async_result);
}
static void
request_cancelled (GCancellable *cancellable,
gpointer user_data)
{
GUnixResolverRequest *req = user_data;
GError *error = NULL;
_g_asyncns_cancel (req->gur->asyncns, req->qy);
req->qy = NULL;
g_cancellable_set_error_if_cancelled (cancellable, &error);
g_simple_async_result_set_from_error (req->async_result, error);
g_error_free (error);
g_unix_resolver_request_complete (req, TRUE);
}
static gboolean
g_unix_resolver_watch (GIOChannel *iochannel,
GIOCondition condition,
gpointer user_data)
{
GUnixResolver *gur = user_data;
_g_asyncns_query_t *qy;
GUnixResolverRequest *req;
if (condition & (G_IO_HUP | G_IO_ERR))
{
/* Shouldn't happen. Should we create a new asyncns? FIXME */
g_warning ("asyncns died");
gur->watch = 0;
return FALSE;
}
while (_g_asyncns_wait (gur->asyncns, FALSE) == 0 &&
(qy = _g_asyncns_getnext (gur->asyncns)) != NULL)
{
req = _g_asyncns_getuserdata (gur->asyncns, qy);
g_unix_resolver_request_complete (req, FALSE);
}
return TRUE;
}
static GUnixResolverRequest *
resolve_async (GUnixResolver *gur,
_g_asyncns_query_t *qy,
GUnixResolverFreeFunc free_func,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data,
gpointer tag)
{
GSimpleAsyncResult *result;
GUnixResolverRequest *req;
result = g_simple_async_result_new (G_OBJECT (gur), callback, user_data, tag);
req = g_unix_resolver_request_new (gur, qy, free_func, cancellable, result);
g_object_unref (result);
_g_asyncns_setuserdata (gur->asyncns, qy, req);
return req;
}
static void
lookup_by_name_free (GUnixResolverRequest *req)
{
g_free (req->u.hostname);
}
static void
lookup_by_name_async (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
GUnixResolverRequest *req;
_g_asyncns_query_t *qy;
qy = _g_asyncns_getaddrinfo (gur->asyncns, hostname, NULL,
&_g_resolver_addrinfo_hints);
req = resolve_async (gur, qy, lookup_by_name_free, cancellable,
callback, user_data, lookup_by_name_async);
req->u.hostname = g_strdup (hostname);
}
static GList *
lookup_by_name_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
GUnixResolverRequest *req;
struct addrinfo *res;
gint retval;
GList *addresses;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
req = g_simple_async_result_get_op_res_gpointer (simple);
retval = _g_asyncns_getaddrinfo_done (req->gur->asyncns, req->qy, &res);
req->qy = NULL;
addresses = _g_resolver_addresses_from_addrinfo (req->u.hostname, res, retval, error);
if (res)
freeaddrinfo (res);
return addresses;
}
static void
lookup_by_address_free (GUnixResolverRequest *req)
{
g_object_unref (req->u.address);
}
static void
lookup_by_address_async (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
GUnixResolverRequest *req;
_g_asyncns_query_t *qy;
struct sockaddr_storage sockaddr;
gsize sockaddr_size;
_g_resolver_address_to_sockaddr (address, &sockaddr, &sockaddr_size);
qy = _g_asyncns_getnameinfo (gur->asyncns,
(struct sockaddr *)&sockaddr, sockaddr_size,
NI_NAMEREQD, TRUE, FALSE);
req = resolve_async (gur, qy, lookup_by_address_free, cancellable,
callback, user_data, lookup_by_address_async);
req->u.address = g_object_ref (address);
}
static gchar *
lookup_by_address_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
GUnixResolverRequest *req;
gchar host[NI_MAXHOST], *name;
gint retval;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
req = g_simple_async_result_get_op_res_gpointer (simple);
retval = _g_asyncns_getnameinfo_done (req->gur->asyncns, req->qy,
host, sizeof (host), NULL, 0);
req->qy = NULL;
name = _g_resolver_name_from_nameinfo (req->u.address, host, retval, error);
return name;
}
static void
lookup_service_free (GUnixResolverRequest *req)
{
g_free (req->u.service);
}
static void
lookup_service_async (GResolver *resolver,
const char *rrname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
GUnixResolverRequest *req;
_g_asyncns_query_t *qy;
qy = _g_asyncns_res_query (gur->asyncns, rrname, C_IN, T_SRV);
req = resolve_async (gur, qy, lookup_service_free, cancellable,
callback, user_data, lookup_service_async);
req->u.service = g_strdup (rrname);
}
static GList *
lookup_service_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
GUnixResolverRequest *req;
guchar *answer;
gint len, herr;
GList *targets;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
req = g_simple_async_result_get_op_res_gpointer (simple);
len = _g_asyncns_res_done (req->gur->asyncns, req->qy, &answer);
req->qy = NULL;
if (len < 0)
herr = h_errno;
else
herr = 0;
targets = _g_resolver_targets_from_res_query (req->u.service, answer, len, herr, error);
_g_asyncns_freeanswer (answer);
return targets;
}
static void
g_unix_resolver_class_init (GUnixResolverClass *unix_class)
{
GResolverClass *resolver_class = G_RESOLVER_CLASS (unix_class);
GObjectClass *object_class = G_OBJECT_CLASS (unix_class);
resolver_class->lookup_by_name_async = lookup_by_name_async;
resolver_class->lookup_by_name_finish = lookup_by_name_finish;
resolver_class->lookup_by_address_async = lookup_by_address_async;
resolver_class->lookup_by_address_finish = lookup_by_address_finish;
resolver_class->lookup_service_async = lookup_service_async;
resolver_class->lookup_service_finish = lookup_service_finish;
object_class->finalize = g_unix_resolver_finalize;
}
#define __G_UNIX_RESOLVER_C__
#include "gioaliasdef.c"

53
gio/gunixresolver.h Normal file
View File

@ -0,0 +1,53 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#ifndef __G_UNIX_RESOLVER_H__
#define __G_UNIX_RESOLVER_H__
#include <gio/gthreadedresolver.h>
#include "libasyncns/asyncns.h"
G_BEGIN_DECLS
#define G_TYPE_UNIX_RESOLVER (g_unix_resolver_get_type ())
#define G_UNIX_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNIX_RESOLVER, GUnixResolver))
#define G_UNIX_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_UNIX_RESOLVER, GUnixResolverClass))
#define G_IS_UNIX_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNIX_RESOLVER))
#define G_IS_UNIX_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_UNIX_RESOLVER))
#define G_UNIX_RESOLVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_UNIX_RESOLVER, GUnixResolverClass))
typedef struct {
GThreadedResolver parent_instance;
_g_asyncns_t *asyncns;
guint watch;
} GUnixResolver;
typedef struct {
GThreadedResolverClass parent_class;
} GUnixResolverClass;
GType g_unix_resolver_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_RESOLVER_H__ */

481
gio/gwin32resolver.c Normal file
View File

@ -0,0 +1,481 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#include "config.h"
#include <glib.h>
#include "glibintl.h"
#include <stdio.h>
#include <string.h>
#include "gwin32resolver.h"
#include "gnetworkingprivate.h"
#include "gcancellable.h"
#include "gsimpleasyncresult.h"
#include "gsocketaddress.h"
#include "gioalias.h"
G_DEFINE_TYPE (GWin32Resolver, g_win32_resolver, G_TYPE_THREADED_RESOLVER)
static void
g_win32_resolver_init (GWin32Resolver *gwr)
{
}
/* This is simpler than GThreadedResolver since we don't have to worry
* about multiple application-level threads, but more complicated than
* GUnixResolver, since we do have to deal with multiple threads of
* our own.
*
* The various request possibilities:
*
* 1. Synchronous: handed off to the base class (GThreadedResolver);
* since it's never possible to cancel a synchronous request in a
* single-threaded program, the request is done in the calling
* thread.
*
* 2. Asynchronous: A GWin32ResolverRequest is created with
* appropriate query-specific information, a Windows event handle,
* and a GSimpleAsyncResult. This is then handed to the
* Windows-internal thread pool, which does the raw DNS query part
* of the operation (being careful to not call any glib methods
* that might fail when called from another thread when
* g_thread_init() has not been called). The main thread sets up a
* GSource to asynchronously poll the event handle. There are two
* sub-possibilities:
*
* a. The resolution completes: the threadpool function calls
* SetEvent() on the event handle and then returns.
*
* b. The resolution is cancelled: request_cancelled()
* disconnects the "cancelled" signal handler, and queues an
* idle handler to complete the async_result.
*
* Since we can't free the request from the threadpool thread
* (because of glib locking issues), we *always* have to have it
* call SetEvent and trigger the callback to indicate that it is
* done. But this means that it's possible for the request to be
* cancelled (queuing an idle handler to return that result) and
* then have the resolution thread complete before the idle handler
* runs. So the event callback and the idle handler need to both
* watch out for this, making sure we don't complete the same
* result twice.
*/
typedef struct GWin32ResolverRequest GWin32ResolverRequest;
typedef void (*GWin32ResolverRequestFreeFunc) (GWin32ResolverRequest *);
struct GWin32ResolverRequest {
GWin32ResolverRequestFreeFunc free_func;
GCancellable *cancellable;
GError *error;
HANDLE *event;
GSimpleAsyncResult *async_result;
gboolean complete;
guint cancelled_idle;
union {
struct {
gchar *name;
gint retval;
struct addrinfo *res;
} name;
struct {
GInetAddress *iaddr;
struct sockaddr_storage addr;
gsize addrlen;
gint retval;
gchar *namebuf;
} address;
struct {
gchar *rrname;
DNS_STATUS retval;
DNS_RECORD *results;
} service;
} u;
};
static GSource *g_win32_handle_source_add (HANDLE handle,
GSourceFunc callback,
gpointer user_data);
static gboolean request_completed (gpointer user_data);
static void request_cancelled (GCancellable *cancellable,
gpointer user_data);
GWin32ResolverRequest *
g_win32_resolver_request_new (GResolver *resolver,
GWin32ResolverRequestFreeFunc free_func,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data,
gpointer tag)
{
GWin32ResolverRequest *req;
req = g_slice_new0 (GWin32ResolverRequest);
req->free_func = free_func;
req->async_result = g_simple_async_result_new (G_OBJECT (resolver), callback,
user_data, tag);
g_simple_async_result_set_op_res_gpointer (req->async_result, req, NULL);
req->event = CreateEvent (NULL, FALSE, FALSE, NULL);
g_win32_handle_source_add (req->event, request_completed, req);
if (cancellable)
{
req->cancellable = g_object_ref (cancellable);
g_signal_connect (cancellable, "cancelled",
G_CALLBACK (request_cancelled), req);
}
return req;
}
static gboolean
request_completed (gpointer user_data)
{
GWin32ResolverRequest *req = user_data;
/* Clean up cancellation-related stuff first */
if (req->cancelled_idle)
{
g_source_remove (req->cancelled_idle);
req->cancelled_idle = 0;
}
if (req->cancellable)
{
g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
g_object_unref (req->cancellable);
}
/* Now complete the result (assuming it wasn't already completed) */
if (req->async_result)
{
g_simple_async_result_complete (req->async_result);
g_object_unref (req->async_result);
}
/* And free req */
CloseHandle (req->event);
req->free_func (req);
g_slice_free (GWin32ResolverRequest, req);
return FALSE;
}
static gboolean
request_cancelled_idle (gpointer user_data)
{
GWin32ResolverRequest *req = user_data;
GError *error = NULL;
req->cancelled_idle = 0;
g_cancellable_set_error_if_cancelled (req->cancellable, &error);
g_simple_async_result_set_from_error (req->async_result, error);
g_simple_async_result_complete (req->async_result);
g_object_unref (req->async_result);
req->async_result = NULL;
/* request_completed will eventually be called to free req */
return FALSE;
}
static void
request_cancelled (GCancellable *cancellable,
gpointer user_data)
{
GWin32ResolverRequest *req = user_data;
if (req->cancellable)
{
g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
g_object_unref (req->cancellable);
req->cancellable = NULL;
}
/* We need to wait until main-loop-time to actually complete the
* result; we don't use _complete_in_idle() here because we need to
* keep track of the source id.
*/
req->cancelled_idle = g_idle_add (request_cancelled_idle, req);
}
static DWORD WINAPI
lookup_by_name_in_thread (LPVOID data)
{
GWin32ResolverRequest *req = data;
req->u.name.retval = getaddrinfo (req->u.name.name, NULL,
&_g_resolver_addrinfo_hints,
&req->u.name.res);
SetEvent (req->event);
return 0;
}
static void
free_lookup_by_name (GWin32ResolverRequest *req)
{
g_free (req->u.name.name);
if (req->u.name.res)
freeaddrinfo (req->u.name.res);
}
static void
lookup_by_name_async (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GWin32ResolverRequest *req;
req = g_win32_resolver_request_new (resolver, free_lookup_by_name,
cancellable, callback, user_data,
lookup_by_name_async);
req->u.name.name = g_strdup (hostname);
QueueUserWorkItem (lookup_by_name_in_thread, req, 0);
}
static GList *
lookup_by_name_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
GWin32ResolverRequest *req;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), NULL);
simple = G_SIMPLE_ASYNC_RESULT (result);
req = g_simple_async_result_get_op_res_gpointer (simple);
return _g_resolver_addresses_from_addrinfo (req->u.name.name, req->u.name.res,
req->u.name.retval, error);
}
static DWORD WINAPI
lookup_by_addresses_in_thread (LPVOID data)
{
GWin32ResolverRequest *req = data;
req->u.address.retval =
getnameinfo ((struct sockaddr *)&req->u.address.addr,
req->u.address.addrlen,
req->u.address.namebuf, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
SetEvent (req->event);
return 0;
}
static void
free_lookup_by_address (GWin32ResolverRequest *req)
{
g_object_unref (req->u.address.iaddr);
g_free (req->u.address.namebuf);
}
static void
lookup_by_address_async (GResolver *resolver,
GInetAddress *address,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GWin32ResolverRequest *req;
req = g_win32_resolver_request_new (resolver, free_lookup_by_address,
cancellable, callback, user_data,
lookup_by_address_async);
req->u.address.iaddr = g_object_ref (address);
_g_resolver_address_to_sockaddr (address, &req->u.address.addr,
&req->u.address.addrlen);
req->u.address.namebuf = g_malloc (NI_MAXHOST);
QueueUserWorkItem (lookup_by_addresses_in_thread, req, 0);
}
static char *
lookup_by_address_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
GWin32ResolverRequest *req;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), NULL);
simple = G_SIMPLE_ASYNC_RESULT (result);
req = g_simple_async_result_get_op_res_gpointer (simple);
return _g_resolver_name_from_nameinfo (req->u.address.iaddr,
req->u.address.namebuf,
req->u.address.retval, error);
}
static DWORD WINAPI
lookup_service_in_thread (LPVOID data)
{
GWin32ResolverRequest *req = data;
req->u.service.retval =
DnsQuery_A (req->u.service.rrname, DNS_TYPE_SRV, DNS_QUERY_STANDARD,
NULL, &req->u.service.results, NULL);
SetEvent (req->event);
return 0;
}
static void
free_lookup_service (GWin32ResolverRequest *req)
{
g_free (req->u.service.rrname);
if (req->u.service.results)
DnsRecordListFree (req->u.service.results, DnsFreeRecordList);
}
static void
lookup_service_async (GResolver *resolver,
const char *rrname,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GWin32ResolverRequest *req;
req = g_win32_resolver_request_new (resolver, free_lookup_service,
cancellable, callback, user_data,
lookup_service_async);
req->u.service.rrname = g_strdup (rrname);
QueueUserWorkItem (lookup_service_in_thread, req, 0);
}
static GList *
lookup_service_finish (GResolver *resolver,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
GWin32ResolverRequest *req;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), NULL);
simple = G_SIMPLE_ASYNC_RESULT (result);
req = g_simple_async_result_get_op_res_gpointer (simple);
return _g_resolver_targets_from_DnsQuery (req->u.service.rrname,
req->u.service.retval,
req->u.service.results, error);
}
static void
g_win32_resolver_class_init (GWin32ResolverClass *win32_class)
{
GResolverClass *resolver_class = G_RESOLVER_CLASS (win32_class);
resolver_class->lookup_by_name_async = lookup_by_name_async;
resolver_class->lookup_by_name_finish = lookup_by_name_finish;
resolver_class->lookup_by_address_async = lookup_by_address_async;
resolver_class->lookup_by_address_finish = lookup_by_address_finish;
resolver_class->lookup_service_async = lookup_service_async;
resolver_class->lookup_service_finish = lookup_service_finish;
}
/* Windows HANDLE GSource */
typedef struct {
GSource source;
GPollFD pollfd;
} GWin32HandleSource;
static gboolean
g_win32_handle_source_prepare (GSource *source,
gint *timeout)
{
*timeout = -1;
return FALSE;
}
static gboolean
g_win32_handle_source_check (GSource *source)
{
GWin32HandleSource *hsource = (GWin32HandleSource *)source;
return hsource->pollfd.revents;
}
static gboolean
g_win32_handle_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
return (*callback) (user_data);
}
static void
g_win32_handle_source_finalize (GSource *source)
{
;
}
GSourceFuncs g_win32_handle_source_funcs = {
g_win32_handle_source_prepare,
g_win32_handle_source_check,
g_win32_handle_source_dispatch,
g_win32_handle_source_finalize
};
static GSource *
g_win32_handle_source_add (HANDLE handle,
GSourceFunc callback,
gpointer user_data)
{
GWin32HandleSource *hsource;
GSource *source;
source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource));
hsource = (GWin32HandleSource *)source;
hsource->pollfd.fd = (gint)handle;
hsource->pollfd.events = G_IO_IN;
hsource->pollfd.revents = 0;
g_source_add_poll (source, &hsource->pollfd);
g_source_set_callback (source, callback, user_data, NULL);
g_source_attach (source, NULL);
return source;
}
#define __G_WIN32_RESOLVER_C__
#include "gioaliasdef.c"

49
gio/gwin32resolver.h Normal file
View File

@ -0,0 +1,49 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#ifndef __G_WIN32_RESOLVER_H__
#define __G_WIN32_RESOLVER_H__
#include <gio/gthreadedresolver.h>
G_BEGIN_DECLS
#define G_TYPE_WIN32_RESOLVER (g_win32_resolver_get_type ())
#define G_WIN32_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_WIN32_RESOLVER, GWin32Resolver))
#define G_WIN32_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_WIN32_RESOLVER, GWin32ResolverClass))
#define G_IS_WIN32_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_WIN32_RESOLVER))
#define G_IS_WIN32_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_WIN32_RESOLVER))
#define G_WIN32_RESOLVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_WIN32_RESOLVER, GWin32ResolverClass))
typedef struct {
GThreadedResolver parent_instance;
} GWin32Resolver;
typedef struct {
GThreadedResolverClass parent_class;
} GWin32ResolverClass;
GType g_win32_resolver_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_RESOLVER_H__ */

View File

@ -0,0 +1,15 @@
## Process this file with automake to produce Makefile.in
include $(top_srcdir)/Makefile.decl
INCLUDES = -I$(top_srcdir)
noinst_LTLIBRARIES = libasyncns.la
libasyncns_la_SOURCES = \
asyncns.c \
asyncns.h \
g-asyncns.h
libasyncns_la_LIBADD = $(LIBASYNCNS_LIBADD)
EXTRA_DIST += README update.sh

7
gio/libasyncns/README Normal file
View File

@ -0,0 +1,7 @@
The sources are derived from Lennart Poettering's libasyncns library:
http://0pointer.de/lennart/projects/libasyncns/
The 'update.sh' script in this directory, when pointed at
the original sources updates the files in this directory
to the new version

1498
gio/libasyncns/asyncns.c Normal file

File diff suppressed because it is too large Load Diff

163
gio/libasyncns/asyncns.h Normal file
View File

@ -0,0 +1,163 @@
#ifndef fooasyncnshfoo
#define fooasyncnshfoo
/***
This file is part of libasyncns.
Copyright 2005-2008 Lennart Poettering
libasyncns 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.1 of the
License, or (at your option) any later version.
libasyncns 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 libasyncns. If not, see
<http://www.gnu.org/licenses/>.
***/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
/** \mainpage
*
* \section moo Method of operation
*
* To use libasyncns allocate an _g_asyncns_t object with
* _g_asyncns_new(). This will spawn a number of worker threads (or processes, depending on what is available) which
* are subsequently used to process the queries the controlling
* program issues via _g_asyncns_getaddrinfo() and
* _g_asyncns_getnameinfo(). Use _g_asyncns_free() to shut down the worker
* threads/processes.
*
* Since libasyncns may fork off new processes you have to make sure that
* your program is not irritated by spurious SIGCHLD signals.
*/
/** \example asyncns-test.c
* An example program */
#ifdef __cplusplus
extern "C" {
#endif
/** An opaque libasyncns session structure */
typedef struct asyncns _g_asyncns_t;
/** An opaque libasyncns query structure */
typedef struct _g_asyncns_query _g_asyncns_query_t;
/** Allocate a new libasyncns session with n_proc worker processes/threads */
_g_asyncns_t* _g_asyncns_new(unsigned n_proc);
/** Free a libasyncns session. This destroys all attached
* _g_asyncns_query_t objects automatically */
void _g_asyncns_free(_g_asyncns_t *asyncns);
/** Return the UNIX file descriptor to select() for readability
* on. Use this function to integrate libasyncns with your custom main
* loop. */
int _g_asyncns_fd(_g_asyncns_t *asyncns);
/** Process pending responses. After this function is called you can
* get the next completed query object(s) using _g_asyncns_getnext(). If
* block is non-zero wait until at least one response has been
* processed. If block is zero, process all pending responses and
* return. */
int _g_asyncns_wait(_g_asyncns_t *asyncns, int block);
/** Issue a name to address query on the specified session. The
* arguments are compatible with the ones of libc's
* getaddrinfo(3). The function returns a new query object. When the
* query is completed you may retrieve the results using
* _g_asyncns_getaddrinfo_done().*/
_g_asyncns_query_t* _g_asyncns_getaddrinfo(_g_asyncns_t *asyncns, const char *node, const char *service, const struct addrinfo *hints);
/** Retrieve the results of a preceding _g_asyncns_getaddrinfo()
* call. Returns a addrinfo structure and a return value compatible
* with libc's getaddrinfo(3). The query object q is destroyed by this
* call and may not be used any further. Make sure to free the
* returned addrinfo structure with _g_asyncns_freeaddrinfo() and not
* libc's freeaddrinfo(3)! If the query is not completed yet EAI_AGAIN
* is returned.*/
int _g_asyncns_getaddrinfo_done(_g_asyncns_t *asyncns, _g_asyncns_query_t* q, struct addrinfo **ret_res);
/** Issue an address to name query on the specified session. The
* arguments are compatible with the ones of libc's
* getnameinfo(3). The function returns a new query object. When the
* query is completed you may retrieve the results using
* _g_asyncns_getnameinfo_done(). Set gethost (resp. getserv) to non-zero
* if you want to query the hostname (resp. the service name). */
_g_asyncns_query_t* _g_asyncns_getnameinfo(_g_asyncns_t *asyncns, const struct sockaddr *sa, socklen_t salen, int flags, int gethost, int getserv);
/** Retrieve the results of a preceding _g_asyncns_getnameinfo()
* call. Returns the hostname and the service name in ret_host and
* ret_serv. The query object q is destroyed by this call and may not
* be used any further. If the query is not completed yet EAI_AGAIN is
* returned. */
int _g_asyncns_getnameinfo_done(_g_asyncns_t *asyncns, _g_asyncns_query_t* q, char *ret_host, size_t hostlen, char *ret_serv, size_t servlen);
/** Issue a resolver query on the specified session. The arguments are
* compatible with the ones of libc's res_query(3). The function returns a new
* query object. When the query is completed you may retrieve the results using
* _g_asyncns_res_done(). */
_g_asyncns_query_t* _g_asyncns_res_query(_g_asyncns_t *asyncns, const char *dname, int class, int type);
/** Issue an resolver query on the specified session. The arguments are
* compatible with the ones of libc's res_search(3). The function returns a new
* query object. When the query is completed you may retrieve the results using
* _g_asyncns_res_done(). */
_g_asyncns_query_t* _g_asyncns_res_search(_g_asyncns_t *asyncns, const char *dname, int class, int type);
/** Retrieve the results of a preceding _g_asyncns_res_query() or
* _g_asyncns_res_search call. The query object q is destroyed by this
* call and may not be used any further. Returns a pointer to the
* answer of the res_query call. If the query is not completed yet
* -EAGAIN is returned, on failure -errno is returned, otherwise the
* length of answer is returned. Make sure to free the answer is a
* call to _g_asyncns_freeanswer(). */
int _g_asyncns_res_done(_g_asyncns_t *asyncns, _g_asyncns_query_t* q, unsigned char **answer);
/** Return the next completed query object. If no query has been
* completed yet, return NULL. Please note that you need to run
* _g_asyncns_wait() before this function will return sensible data. */
_g_asyncns_query_t* _g_asyncns_getnext(_g_asyncns_t *asyncns);
/** Return the number of query objects (completed or not) attached to
* this session */
int _g_asyncns_getnqueries(_g_asyncns_t *asyncns);
/** Cancel a currently running query. q is is destroyed by this call
* and may not be used any futher. */
void _g_asyncns_cancel(_g_asyncns_t *asyncns, _g_asyncns_query_t* q);
/** Free the addrinfo structure as returned by
* _g_asyncns_getaddrinfo_done(). Make sure to use this functions instead
* of the libc's freeaddrinfo()! */
void _g_asyncns_freeaddrinfo(struct addrinfo *ai);
/** Free the answer data as returned by _g_asyncns_res_done().*/
void _g_asyncns_freeanswer(unsigned char *answer);
/** Returns non-zero when the query operation specified by q has been completed */
int _g_asyncns_isdone(_g_asyncns_t *asyncns, _g_asyncns_query_t*q);
/** Assign some opaque userdata with a query object */
void _g_asyncns_setuserdata(_g_asyncns_t *asyncns, _g_asyncns_query_t *q, void *userdata);
/** Return userdata assigned to a query object. Use
* _g_asyncns_setuserdata() to set this data. If no data has been set
* prior to this call it returns NULL. */
void* _g_asyncns_getuserdata(_g_asyncns_t *asyncns, _g_asyncns_query_t *q);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,28 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#ifndef __G_ASYNCNS_H__
#include "config.h"
#define _GNU_SOURCE
#undef HAVE_PTHREAD
#include "asyncns.h"
#endif

20
gio/libasyncns/update.sh Normal file
View File

@ -0,0 +1,20 @@
#!/bin/sh
if test $# = 1 ; then
ORIGINAL=$1
else
echo "Usage: update.sh /path/to/libasyncns" 1>&2
exit 1
fi
if test -f $ORIGINAL/libasyncns/asyncns.c ; then : ; else
echo "Usage: update.sh /path/to/libasyncns" 1>&2
exit 1
fi
for i in asyncns.c asyncns.h ; do
sed -e 's/\([^a-z]\)asyncns_/\1_g_asyncns_/g' \
-e 's/^asyncns_/_g_asyncns_/' \
-e 's/<config\.h>/"g-asyncns\.h"/' \
$ORIGINAL/libasyncns/$i > $i
done

View File

@ -9,7 +9,7 @@ if ! which readelf 2>/dev/null >/dev/null; then
exit 0
fi
SKIP='\<g_access\|\<g_array_\|\<g_ascii\|\<g_list_\|\<g_assertion_message\|\<g_warn_message\|\<g_atomic\|\<g_build_filename\|\<g_byte_array\|\<g_child_watch\|\<g_convert\|\<g_dir_\|\<g_error_\|\<g_clear_error\|\<g_file_error_quark\|\<g_file_get_contents\|\<g_file_set_contents\|\<g_file_test\|\<g_file_read_link\|\<g_filename_\|\<g_find_program_in_path\|\<g_free\|\<g_get_\|\<g_getenv\|\<g_hash_table_\|\<g_idle_\|\<g_intern_static_string\|\<g_io_channel_\|\<g_key_file_\|\<g_listenv\|\<g_locale_to_utf8\|\<g_log\|\<g_main_context_wakeup\|\<g_malloc\|\<g_markup_\|\<g_mkdir_\|\<g_mkstemp\|\<g_module_\|\<g_object_\|\<g_once_\|\<g_param_spec_\|\<g_path_\|\<g_printerr\|\<g_propagate_error\|\<g_ptr_array_\|\<g_qsort_\|\<g_quark_\|\<g_queue_\|\<g_realloc\|\<g_return_if_fail\|\<g_set_error\|\<g_shell_\|\<g_signal_\|\<g_slice_\|\<g_slist_\|\<g_snprintf\|\<g_source_\|\<g_spawn_\|\<g_static_\|\<g_str\|\<g_thread_pool_\|\<g_time_val_add\|\<g_timeout_\|\<g_type_\|\<g_unlink\|\<g_uri_\|\<g_utf8_\|\<g_value_\|\<g_enum_\|\<g_flags_\|\<g_checksum\|\<g_io_add_watch\|\<g_bit_\|\<g_poll\|\<g_boxed'
SKIP='\<g_access\|\<g_array_\|\<g_ascii\|\<g_list_\|\<g_assertion_message\|\<g_warn_message\|\<g_atomic\|\<g_bit_\|\<g_boxed\|\<g_build_filename\|\<g_byte_array\|\<g_checksum\|\<g_child_watch\|\<g_clear_error\|\<g_convert\|\<g_dir_\|\<g_enum_\|\<g_error_\|\<g_file_error_quark\|\<g_file_get_contents\|\<g_file_set_contents\|\<g_file_test\|\<g_file_read_link\|\<g_filename_\|\<g_find_program_in_path\|\<g_flags_\|\<g_free\|\<g_get_\|\<g_getenv\|\<g_hash_table_\|\<g_hostname_\|\<g_idle_\|\<g_intern_static_string\|\<g_io_add_watch\|\<g_io_channel_\|\<g_key_file_\|\<g_listenv\|\<g_locale_to_utf8\|\<g_log\|\<g_main_context_wakeup\|\<g_malloc\|\<g_markup_\|\<g_mkdir_\|\<g_mkstemp\|\<g_module_\|\<g_object_\|\<g_once_\|\<g_param_spec_\|\<g_path_\|\<g_poll\|\<g_printerr\|\<g_propagate_error\|\<g_ptr_array_\|\<g_qsort_\|\<g_quark_\|\<g_queue_\|\<g_random_int_range\|\<g_realloc\|\<g_return_if_fail\|\<g_set_error\|\<g_shell_\|\<g_signal_\|\<g_slice_\|\<g_slist_\|\<g_snprintf\|\<g_source_\|\<g_spawn_\|\<g_static_\|\<g_str\|\<g_thread_pool_\|\<g_time_val_add\|\<g_timeout_\|\<g_type_\|\<g_unlink\|\<g_uri_\|\<g_utf8_\|\<g_value_'
for so in .libs/lib*.so; do
echo Checking $so for local PLT entries

View File

@ -12,3 +12,4 @@ memory-input-stream
memory-output-stream
filter-streams
sleepy-stream
resolver

View File

@ -9,7 +9,7 @@ INCLUDES = \
-I$(top_srcdir)/gio \
$(GLIB_DEBUG_FLAGS)
noinst_PROGRAMS = $(TEST_PROGS)
noinst_PROGRAMS = $(TEST_PROGS) $(SAMPLE_PROGS)
progs_ldadd = \
$(top_builddir)/glib/libglib-2.0.la \
$(top_builddir)/gobject/libgobject-2.0.la \
@ -29,6 +29,8 @@ TEST_PROGS += \
filter-streams \
simple-async-result
SAMPLE_PROGS = resolver
if OS_UNIX
TEST_PROGS += live-g-file unix-streams desktop-app-info
endif
@ -76,4 +78,8 @@ sleepy_stream_LDADD = $(progs_ldadd)
filter_streams_SOURCES = filter-streams.c
filter_streams_LDADD = $(progs_ldadd)
resolver_SOURCES = resolver.c
resolver_LDADD = $(progs_ldadd) \
$(top_builddir)/gthread/libgthread-2.0.la
DISTCLEAN_FILES = applications/mimeinfo.cache

377
gio/tests/resolver.c Normal file
View File

@ -0,0 +1,377 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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.
*/
#include "config.h"
#include <glib.h>
#include "glibintl.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <gio/gio.h>
static GResolver *resolver;
static GCancellable *cancellable;
static GMainLoop *loop;
static int nlookups = 0;
static void
usage (void)
{
fprintf (stderr, "Usage: resolver [-t] [-s] [hostname | IP | service/protocol/domain ] ...\n");
fprintf (stderr, " Use -t to enable threading.\n");
fprintf (stderr, " Use -s to do synchronous lookups.\n");
fprintf (stderr, " Both together will result in simultaneous lookups in multiple threads\n");
exit (1);
}
G_LOCK_DEFINE_STATIC (response);
static void
done_lookup (void)
{
nlookups--;
if (nlookups == 0)
{
/* In the sync case we need to make sure we don't call
* g_main_loop_quit before the loop is actually running...
*/
g_idle_add ((GSourceFunc)g_main_loop_quit, loop);
}
}
static void
print_resolved_name (const char *phys,
char *name,
GError *error)
{
G_LOCK (response);
printf ("Address: %s\n", phys);
if (error)
{
printf ("Error: %s\n", error->message);
g_error_free (error);
}
else
{
printf ("Name: %s\n", name);
g_free (name);
}
printf ("\n");
done_lookup ();
G_UNLOCK (response);
}
static void
print_resolved_addresses (const char *name,
GList *addresses,
GError *error)
{
char *phys;
GList *a;
G_LOCK (response);
printf ("Name: %s\n", name);
if (error)
{
printf ("Error: %s\n", error->message);
g_error_free (error);
}
else
{
for (a = addresses; a; a = a->next)
{
phys = g_inet_address_to_string (a->data);
printf ("Address: %s\n", phys);
g_free (phys);
g_object_unref (a->data);
}
g_list_free (addresses);
}
printf ("\n");
done_lookup ();
G_UNLOCK (response);
}
static void
print_resolved_service (const char *service,
GList *targets,
GError *error)
{
GList *t;
G_LOCK (response);
printf ("Service: %s\n", service);
if (error)
{
printf ("Error: %s\n", error->message);
g_error_free (error);
}
else
{
for (t = targets; t; t = t->next)
{
printf ("%s:%u (pri %u, weight %u)\n",
g_srv_target_get_hostname (t->data),
g_srv_target_get_port (t->data),
g_srv_target_get_priority (t->data),
g_srv_target_get_weight (t->data));
g_srv_target_free (t->data);
}
g_list_free (targets);
}
printf ("\n");
done_lookup ();
G_UNLOCK (response);
}
static void
lookup_one_sync (const char *arg)
{
GError *error = NULL;
if (strchr (arg, '/'))
{
GList *targets;
/* service/protocol/domain */
char **parts = g_strsplit (arg, "/", 3);
if (!parts || !parts[2])
usage ();
targets = g_resolver_lookup_service (resolver,
parts[0], parts[1], parts[2],
cancellable, &error);
print_resolved_service (arg, targets, error);
}
else if (g_hostname_is_ip_address (arg))
{
GInetAddress *addr = g_inet_address_new_from_string (arg);
char *name;
name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
print_resolved_name (arg, name, error);
g_object_unref (addr);
}
else
{
GList *addresses;
addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
print_resolved_addresses (arg, addresses, error);
}
}
static gpointer
lookup_thread (gpointer arg)
{
lookup_one_sync (arg);
return NULL;
}
static void
start_threaded_lookups (char **argv, int argc)
{
int i;
for (i = 0; i < argc; i++)
g_thread_create (lookup_thread, argv[i], FALSE, NULL);
}
static void
start_sync_lookups (char **argv, int argc)
{
int i;
for (i = 0; i < argc; i++)
lookup_one_sync (argv[i]);
}
static void
lookup_by_addr_callback (GObject *source, GAsyncResult *result,
gpointer user_data)
{
const char *phys = user_data;
GError *error = NULL;
char *name;
name = g_resolver_lookup_by_address_finish (resolver, result, &error);
print_resolved_name (phys, name, error);
}
static void
lookup_by_name_callback (GObject *source, GAsyncResult *result,
gpointer user_data)
{
const char *name = user_data;
GError *error = NULL;
GList *addresses;
addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
print_resolved_addresses (name, addresses, error);
}
static void
lookup_service_callback (GObject *source, GAsyncResult *result,
gpointer user_data)
{
const char *service = user_data;
GError *error = NULL;
GList *targets;
targets = g_resolver_lookup_service_finish (resolver, result, &error);
print_resolved_service (service, targets, error);
}
static void
start_async_lookups (char **argv, int argc)
{
int i;
for (i = 0; i < argc; i++)
{
if (strchr (argv[i], '/'))
{
/* service/protocol/domain */
char **parts = g_strsplit (argv[i], "/", 3);
if (!parts || !parts[2])
usage ();
g_resolver_lookup_service_async (resolver,
parts[0], parts[1], parts[2],
cancellable,
lookup_service_callback, argv[i]);
}
else if (g_hostname_is_ip_address (argv[i]))
{
GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
g_resolver_lookup_by_address_async (resolver, addr, cancellable,
lookup_by_addr_callback, argv[i]);
g_object_unref (addr);
}
else
{
g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
lookup_by_name_callback,
argv[i]);
}
}
}
#ifdef G_OS_UNIX
static int cancel_fds[2];
static void
interrupted (int sig)
{
signal (SIGINT, SIG_DFL);
write (cancel_fds[1], "x", 1);
}
static gboolean
async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancellable)
{
g_cancellable_cancel (cancellable);
return FALSE;
}
#endif
int
main (int argc, char **argv)
{
gboolean threaded = FALSE, synchronous = FALSE;
#ifdef G_OS_UNIX
GIOChannel *chan;
guint watch;
#endif
/* We can't use GOptionContext because we use the arguments to
* decide whether or not to call g_thread_init().
*/
while (argc >= 2 && argv[1][0] == '-')
{
if (!strcmp (argv[1], "-t"))
{
g_thread_init (NULL);
threaded = TRUE;
}
else if (!strcmp (argv[1], "-s"))
synchronous = TRUE;
else
usage ();
argv++;
argc--;
}
g_type_init ();
if (argc < 2)
usage ();
resolver = g_resolver_get_default ();
cancellable = g_cancellable_new ();
#ifdef G_OS_UNIX
/* Set up cancellation; we want to cancel if the user ^C's the
* program, but we can't cancel directly from an interrupt.
*/
signal (SIGINT, interrupted);
if (pipe (cancel_fds) == -1)
{
perror ("pipe");
exit (1);
}
chan = g_io_channel_unix_new (cancel_fds[0]);
watch = g_io_add_watch (chan, G_IO_IN, async_cancel, cancellable);
g_io_channel_unref (chan);
#endif
nlookups = argc - 1;
loop = g_main_loop_new (NULL, TRUE);
if (threaded && synchronous)
start_threaded_lookups (argv + 1, argc - 1);
else if (synchronous)
start_sync_lookups (argv + 1, argc - 1);
else
start_async_lookups (argv + 1, argc - 1);
g_main_run (loop);
g_main_loop_unref (loop);
#ifdef G_OS_UNIX
g_source_remove (watch);
#endif
g_object_unref (cancellable);
return 0;
}