mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-24 14:36:13 +01:00
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:
parent
68fc055627
commit
c94d3f9288
25
configure.in
25
configure.in
@ -974,6 +974,30 @@ if $glib_failed ; then
|
|||||||
AC_MSG_ERROR([Could not determine values for AF_INET* constants])
|
AC_MSG_ERROR([Could not determine values for AF_INET* constants])
|
||||||
fi
|
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
|
||||||
dnl if statfs() takes 2 arguments (Posix) or 4 (Solaris)
|
dnl if statfs() takes 2 arguments (Posix) or 4 (Solaris)
|
||||||
dnl
|
dnl
|
||||||
@ -3384,6 +3408,7 @@ gthread/Makefile
|
|||||||
gio/Makefile
|
gio/Makefile
|
||||||
gio/xdgmime/Makefile
|
gio/xdgmime/Makefile
|
||||||
gio/inotify/Makefile
|
gio/inotify/Makefile
|
||||||
|
gio/libasyncns/Makefile
|
||||||
gio/fen/Makefile
|
gio/fen/Makefile
|
||||||
gio/fam/Makefile
|
gio/fam/Makefile
|
||||||
gio/win32/Makefile
|
gio/win32/Makefile
|
||||||
|
@ -43,11 +43,14 @@ IGNORE_HFILES= \
|
|||||||
glocalvfs.h \
|
glocalvfs.h \
|
||||||
gnativevolumemonitor.h \
|
gnativevolumemonitor.h \
|
||||||
gpollfilemonitor.h \
|
gpollfilemonitor.h \
|
||||||
|
gthreadedresolver.h \
|
||||||
gunionvolumemonitor.h \
|
gunionvolumemonitor.h \
|
||||||
gunixdrive.h \
|
gunixdrive.h \
|
||||||
|
gunixresolver.h \
|
||||||
gunixvolume.h \
|
gunixvolume.h \
|
||||||
gvolumeprivate.h \
|
gvolumeprivate.h \
|
||||||
gwin32appinfo.h \
|
gwin32appinfo.h \
|
||||||
|
gwin32resolver.h \
|
||||||
inotify-diag.h \
|
inotify-diag.h \
|
||||||
inotify-helper.h \
|
inotify-helper.h \
|
||||||
inotify-kernel.h \
|
inotify-kernel.h \
|
||||||
@ -85,7 +88,8 @@ GTKDOC_LIBS = \
|
|||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# Extra options to supply to gtkdoc-mkdb
|
# 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
|
# Images to copy into HTML directory
|
||||||
HTML_IMAGES = \
|
HTML_IMAGES = \
|
||||||
|
@ -92,10 +92,12 @@
|
|||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="networking">
|
<chapter id="networking">
|
||||||
<title>Networking</title>
|
<title>Networking</title>
|
||||||
|
<xi:include href="xml/gresolver.xml"/>
|
||||||
<xi:include href="xml/ginetaddress.xml"/>
|
<xi:include href="xml/ginetaddress.xml"/>
|
||||||
<xi:include href="xml/gsocketaddress.xml"/>
|
<xi:include href="xml/gsocketaddress.xml"/>
|
||||||
<xi:include href="xml/ginetsocketaddress.xml"/>
|
<xi:include href="xml/ginetsocketaddress.xml"/>
|
||||||
<xi:include href="xml/gunixsocketaddress.xml"/>
|
<xi:include href="xml/gunixsocketaddress.xml"/>
|
||||||
|
<xi:include href="xml/gsrvtarget.xml"/>
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="utils">
|
<chapter id="utils">
|
||||||
<title>Utilities</title>
|
<title>Utilities</title>
|
||||||
|
@ -1404,3 +1404,55 @@ G_UNIX_SOCKET_ADDRESS_GET_CLASS
|
|||||||
<SUBSECTION Private>
|
<SUBSECTION Private>
|
||||||
g_unix_socket_address_get_type
|
g_unix_socket_address_get_type
|
||||||
</SECTION>
|
</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>
|
||||||
|
@ -55,6 +55,7 @@ g_native_volume_monitor_get_type
|
|||||||
g_output_stream_get_type
|
g_output_stream_get_type
|
||||||
g_output_stream_splice_flags_get_type
|
g_output_stream_splice_flags_get_type
|
||||||
g_password_save_get_type
|
g_password_save_get_type
|
||||||
|
g_resolver_get_type
|
||||||
g_seekable_get_type
|
g_seekable_get_type
|
||||||
g_simple_async_result_get_type
|
g_simple_async_result_get_type
|
||||||
g_socket_address_get_type
|
g_socket_address_get_type
|
||||||
|
@ -5,7 +5,7 @@ NULL =
|
|||||||
SUBDIRS=
|
SUBDIRS=
|
||||||
|
|
||||||
if OS_UNIX
|
if OS_UNIX
|
||||||
SUBDIRS += xdgmime
|
SUBDIRS += libasyncns xdgmime
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if OS_WIN32_AND_DLL_COMPILATION
|
if OS_WIN32_AND_DLL_COMPILATION
|
||||||
@ -125,13 +125,15 @@ endif
|
|||||||
|
|
||||||
if OS_UNIX
|
if OS_UNIX
|
||||||
appinfo_sources += gdesktopappinfo.c gdesktopappinfo.h
|
appinfo_sources += gdesktopappinfo.c gdesktopappinfo.h
|
||||||
platform_libadd += xdgmime/libxdgmime.la
|
platform_libadd += libasyncns/libasyncns.la xdgmime/libxdgmime.la
|
||||||
platform_deps += xdgmime/libxdgmime.la
|
platform_deps += libasyncns/libasyncns.la xdgmime/libxdgmime.la
|
||||||
unix_sources = \
|
unix_sources = \
|
||||||
gunixmount.c \
|
gunixmount.c \
|
||||||
gunixmount.h \
|
gunixmount.h \
|
||||||
gunixmounts.c \
|
gunixmounts.c \
|
||||||
gunixmounts.h \
|
gunixmounts.h \
|
||||||
|
gunixresolver.c \
|
||||||
|
gunixresolver.h \
|
||||||
gunixsocketaddress.c \
|
gunixsocketaddress.c \
|
||||||
gunixvolume.c \
|
gunixvolume.c \
|
||||||
gunixvolume.h \
|
gunixvolume.h \
|
||||||
@ -154,10 +156,12 @@ endif
|
|||||||
|
|
||||||
if OS_WIN32
|
if OS_WIN32
|
||||||
appinfo_sources += gwin32appinfo.c gwin32appinfo.h
|
appinfo_sources += gwin32appinfo.c gwin32appinfo.h
|
||||||
platform_libadd += -lshlwapi -lws2_32
|
platform_libadd += -lshlwapi -lws2_32 -ldnsapi
|
||||||
win32_sources = \
|
win32_sources = \
|
||||||
gwin32mount.c \
|
gwin32mount.c \
|
||||||
gwin32mount.h \
|
gwin32mount.h \
|
||||||
|
gwin32resolver.c \
|
||||||
|
gwin32resolver.h \
|
||||||
gwin32volumemonitor.c \
|
gwin32volumemonitor.c \
|
||||||
gwin32volumemonitor.h \
|
gwin32volumemonitor.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
@ -217,10 +221,14 @@ libgio_2_0_la_SOURCES = \
|
|||||||
goutputstream.c \
|
goutputstream.c \
|
||||||
gpollfilemonitor.c \
|
gpollfilemonitor.c \
|
||||||
gpollfilemonitor.h \
|
gpollfilemonitor.h \
|
||||||
|
gresolver.c \
|
||||||
gseekable.c \
|
gseekable.c \
|
||||||
gsimpleasyncresult.c \
|
gsimpleasyncresult.c \
|
||||||
gsocketaddress.c \
|
gsocketaddress.c \
|
||||||
|
gsrvtarget.c \
|
||||||
gthemedicon.c \
|
gthemedicon.c \
|
||||||
|
gthreadedresolver.c \
|
||||||
|
gthreadedresolver.h \
|
||||||
gunionvolumemonitor.c \
|
gunionvolumemonitor.c \
|
||||||
gunionvolumemonitor.h \
|
gunionvolumemonitor.h \
|
||||||
gvfs.c \
|
gvfs.c \
|
||||||
@ -321,9 +329,11 @@ gio_headers = \
|
|||||||
gmountoperation.h \
|
gmountoperation.h \
|
||||||
gnativevolumemonitor.h \
|
gnativevolumemonitor.h \
|
||||||
goutputstream.h \
|
goutputstream.h \
|
||||||
|
gresolver.h \
|
||||||
gseekable.h \
|
gseekable.h \
|
||||||
gsimpleasyncresult.h \
|
gsimpleasyncresult.h \
|
||||||
gsocketaddress.h \
|
gsocketaddress.h \
|
||||||
|
gsrvtarget.h \
|
||||||
gthemedicon.h \
|
gthemedicon.h \
|
||||||
gvfs.h \
|
gvfs.h \
|
||||||
gvolume.h \
|
gvolume.h \
|
||||||
|
@ -36,7 +36,12 @@
|
|||||||
* SECTION:ginetaddress
|
* SECTION:ginetaddress
|
||||||
* @short_description: An IPv4/IPv6 address
|
* @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
|
* To actually connect to a remote host, you will need a
|
||||||
* #GInetSocketAddress (which includes a #GInetAddress as well as a
|
* #GInetSocketAddress (which includes a #GInetAddress as well as a
|
||||||
|
@ -64,9 +64,11 @@
|
|||||||
#include <gio/gmountoperation.h>
|
#include <gio/gmountoperation.h>
|
||||||
#include <gio/gnativevolumemonitor.h>
|
#include <gio/gnativevolumemonitor.h>
|
||||||
#include <gio/goutputstream.h>
|
#include <gio/goutputstream.h>
|
||||||
|
#include <gio/gresolver.h>
|
||||||
#include <gio/gseekable.h>
|
#include <gio/gseekable.h>
|
||||||
#include <gio/gsimpleasyncresult.h>
|
#include <gio/gsimpleasyncresult.h>
|
||||||
#include <gio/gsocketaddress.h>
|
#include <gio/gsocketaddress.h>
|
||||||
|
#include <gio/gsrvtarget.h>
|
||||||
#include <gio/gthemedicon.h>
|
#include <gio/gthemedicon.h>
|
||||||
#include <gio/gvfs.h>
|
#include <gio/gvfs.h>
|
||||||
#include <gio/gvolume.h>
|
#include <gio/gvolume.h>
|
||||||
|
@ -834,6 +834,7 @@ g_ask_password_flags_get_type G_GNUC_CONST
|
|||||||
g_password_save_get_type G_GNUC_CONST
|
g_password_save_get_type G_GNUC_CONST
|
||||||
g_emblem_origin_get_type G_GNUC_CONST
|
g_emblem_origin_get_type G_GNUC_CONST
|
||||||
g_socket_family_get_type G_GNUC_CONST
|
g_socket_family_get_type G_GNUC_CONST
|
||||||
|
g_resolver_error_get_type G_GNUC_CONST
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -908,3 +909,59 @@ g_socket_address_get_native_size
|
|||||||
g_socket_address_to_native
|
g_socket_address_to_native
|
||||||
#endif
|
#endif
|
||||||
#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
|
||||||
|
@ -476,6 +476,20 @@ typedef enum {
|
|||||||
G_EMBLEM_ORIGIN_TAG
|
G_EMBLEM_ORIGIN_TAG
|
||||||
} GEmblemOrigin;
|
} 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:
|
* GSocketFamily:
|
||||||
|
@ -99,12 +99,12 @@ typedef struct _GMemoryOutputStream GMemoryOutputStream;
|
|||||||
**/
|
**/
|
||||||
typedef struct _GMount GMount; /* Dummy typedef */
|
typedef struct _GMount GMount; /* Dummy typedef */
|
||||||
typedef struct _GMountOperation GMountOperation;
|
typedef struct _GMountOperation GMountOperation;
|
||||||
typedef struct _GNetworkAddress GNetworkAddress;
|
|
||||||
typedef struct _GNetworkService GNetworkService;
|
|
||||||
typedef struct _GOutputStream GOutputStream;
|
typedef struct _GOutputStream GOutputStream;
|
||||||
|
typedef struct _GResolver GResolver;
|
||||||
typedef struct _GSeekable GSeekable;
|
typedef struct _GSeekable GSeekable;
|
||||||
typedef struct _GSimpleAsyncResult GSimpleAsyncResult;
|
typedef struct _GSimpleAsyncResult GSimpleAsyncResult;
|
||||||
typedef struct _GSocketAddress GSocketAddress;
|
typedef struct _GSocketAddress GSocketAddress;
|
||||||
|
typedef struct _GSrvTarget GSrvTarget;
|
||||||
typedef struct _GThemedIcon GThemedIcon;
|
typedef struct _GThemedIcon GThemedIcon;
|
||||||
typedef struct _GVfs GVfs; /* Dummy typedef */
|
typedef struct _GVfs GVfs; /* Dummy typedef */
|
||||||
|
|
||||||
|
@ -50,4 +50,36 @@
|
|||||||
|
|
||||||
#endif
|
#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__ */
|
#endif /* __G_NETWORKINGPRIVATE_H__ */
|
||||||
|
855
gio/gresolver.c
Normal file
855
gio/gresolver.c
Normal 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
159
gio/gresolver.h
Normal 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
334
gio/gsrvtarget.c
Normal 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
52
gio/gsrvtarget.h
Normal 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
617
gio/gthreadedresolver.c
Normal 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
50
gio/gthreadedresolver.h
Normal 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
433
gio/gunixresolver.c
Normal 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
53
gio/gunixresolver.h
Normal 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
481
gio/gwin32resolver.c
Normal 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
49
gio/gwin32resolver.h
Normal 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__ */
|
15
gio/libasyncns/Makefile.am
Normal file
15
gio/libasyncns/Makefile.am
Normal 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
7
gio/libasyncns/README
Normal 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
1498
gio/libasyncns/asyncns.c
Normal file
File diff suppressed because it is too large
Load Diff
163
gio/libasyncns/asyncns.h
Normal file
163
gio/libasyncns/asyncns.h
Normal 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
|
28
gio/libasyncns/g-asyncns.h
Normal file
28
gio/libasyncns/g-asyncns.h
Normal 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
20
gio/libasyncns/update.sh
Normal 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
|
@ -9,7 +9,7 @@ if ! which readelf 2>/dev/null >/dev/null; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
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
|
for so in .libs/lib*.so; do
|
||||||
echo Checking $so for local PLT entries
|
echo Checking $so for local PLT entries
|
||||||
|
1
gio/tests/.gitignore
vendored
1
gio/tests/.gitignore
vendored
@ -12,3 +12,4 @@ memory-input-stream
|
|||||||
memory-output-stream
|
memory-output-stream
|
||||||
filter-streams
|
filter-streams
|
||||||
sleepy-stream
|
sleepy-stream
|
||||||
|
resolver
|
||||||
|
@ -9,7 +9,7 @@ INCLUDES = \
|
|||||||
-I$(top_srcdir)/gio \
|
-I$(top_srcdir)/gio \
|
||||||
$(GLIB_DEBUG_FLAGS)
|
$(GLIB_DEBUG_FLAGS)
|
||||||
|
|
||||||
noinst_PROGRAMS = $(TEST_PROGS)
|
noinst_PROGRAMS = $(TEST_PROGS) $(SAMPLE_PROGS)
|
||||||
progs_ldadd = \
|
progs_ldadd = \
|
||||||
$(top_builddir)/glib/libglib-2.0.la \
|
$(top_builddir)/glib/libglib-2.0.la \
|
||||||
$(top_builddir)/gobject/libgobject-2.0.la \
|
$(top_builddir)/gobject/libgobject-2.0.la \
|
||||||
@ -29,6 +29,8 @@ TEST_PROGS += \
|
|||||||
filter-streams \
|
filter-streams \
|
||||||
simple-async-result
|
simple-async-result
|
||||||
|
|
||||||
|
SAMPLE_PROGS = resolver
|
||||||
|
|
||||||
if OS_UNIX
|
if OS_UNIX
|
||||||
TEST_PROGS += live-g-file unix-streams desktop-app-info
|
TEST_PROGS += live-g-file unix-streams desktop-app-info
|
||||||
endif
|
endif
|
||||||
@ -76,4 +78,8 @@ sleepy_stream_LDADD = $(progs_ldadd)
|
|||||||
filter_streams_SOURCES = filter-streams.c
|
filter_streams_SOURCES = filter-streams.c
|
||||||
filter_streams_LDADD = $(progs_ldadd)
|
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
|
DISTCLEAN_FILES = applications/mimeinfo.cache
|
||||||
|
377
gio/tests/resolver.c
Normal file
377
gio/tests/resolver.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user