glib/gio/tests/resolver.c
Dan Winship 5a30712dc7 Remove !g_thread_supported() codepaths in gio
In particular, remove the libasyncns import, which was only used by
GUnixResolver, which is only used when threads are not available.
Likewise remove GWin32Resolver, and the hacky broken non-threaded
parts of GIOScheduler.

https://bugzilla.gnome.org/show_bug.cgi?id=616754
2011-09-09 12:47:39 -04:00

506 lines
12 KiB
C

/* -*- 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 G_GNUC_NORETURN
usage (void)
{
fprintf (stderr, "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n");
fprintf (stderr, " resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
fprintf (stderr, " Use -s to do synchronous lookups.\n");
fprintf (stderr, " Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n");
fprintf (stderr, " The given NUMBER determines how many times the connectable will be enumerated.\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_sync_lookups (char **argv, int argc)
{
int i;
for (i = 0; i < argc; i++)
g_thread_create (lookup_thread, argv[i], FALSE, NULL);
}
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]);
}
/* Stress-test the reloading code */
g_signal_emit_by_name (resolver, "reload");
}
}
static void
print_connectable_sockaddr (GSocketAddress *sockaddr,
GError *error)
{
char *phys;
if (error)
{
printf ("Error: %s\n", error->message);
g_error_free (error);
}
else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
{
printf ("Error: Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
g_object_unref (sockaddr);
}
else
{
GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr);
phys = g_inet_address_to_string (g_inet_socket_address_get_address (isa));
printf ("Address: %s%s%s:%d\n",
strchr (phys, ':') ? "[" : "", phys, strchr (phys, ':') ? "]" : "",
g_inet_socket_address_get_port (isa));
g_free (phys);
g_object_unref (sockaddr);
}
}
static void
do_sync_connectable (GSocketAddressEnumerator *enumerator)
{
GSocketAddress *sockaddr;
GError *error = NULL;
while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
print_connectable_sockaddr (sockaddr, error);
g_object_unref (enumerator);
done_lookup ();
}
static void do_async_connectable (GSocketAddressEnumerator *enumerator);
static void
got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
{
GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
GSocketAddress *sockaddr;
GError *error = NULL;
sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
if (sockaddr || error)
print_connectable_sockaddr (sockaddr, error);
if (sockaddr)
do_async_connectable (enumerator);
else
{
g_object_unref (enumerator);
done_lookup ();
}
}
static void
do_async_connectable (GSocketAddressEnumerator *enumerator)
{
g_socket_address_enumerator_next_async (enumerator, cancellable,
got_next_async, NULL);
}
static void
do_connectable (const char *arg, gboolean synchronous, guint count)
{
char **parts;
GSocketConnectable *connectable;
GSocketAddressEnumerator *enumerator;
if (strchr (arg, '/'))
{
/* service/protocol/domain */
parts = g_strsplit (arg, "/", 3);
if (!parts || !parts[2])
usage ();
connectable = g_network_service_new (parts[0], parts[1], parts[2]);
}
else
{
guint16 port;
parts = g_strsplit (arg, ":", 2);
if (parts && parts[1])
{
arg = parts[0];
port = strtoul (parts[1], NULL, 10);
}
else
port = 0;
if (g_hostname_is_ip_address (arg))
{
GInetAddress *addr = g_inet_address_new_from_string (arg);
GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
g_object_unref (addr);
connectable = G_SOCKET_CONNECTABLE (sockaddr);
}
else
connectable = g_network_address_new (arg, port);
}
while (count--)
{
enumerator = g_socket_connectable_enumerate (connectable);
if (synchronous)
do_sync_connectable (enumerator);
else
do_async_connectable (enumerator);
}
g_object_unref (connectable);
}
#ifdef G_OS_UNIX
static int cancel_fds[2];
static void
interrupted (int sig)
{
gssize c;
signal (SIGINT, SIG_DFL);
c = write (cancel_fds[1], "x", 1);
g_assert_cmpint(c, ==, 1);
}
static gboolean
async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
{
g_cancellable_cancel (cancel);
return FALSE;
}
#endif
int
main (int argc, char **argv)
{
gboolean synchronous = FALSE;
guint connectable_count = 0;
#ifdef G_OS_UNIX
GIOChannel *chan;
guint watch;
#endif
g_type_init ();
/* FIXME: use GOptionContext */
while (argc >= 2 && argv[1][0] == '-')
{
if (!strcmp (argv[1], "-s"))
synchronous = TRUE;
else if (!strcmp (argv[1], "-c"))
{
connectable_count = atoi (argv[2]);
argv++;
argc--;
}
else
usage ();
argv++;
argc--;
}
if (argc < 2 || (argc > 2 && connectable_count))
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 (connectable_count)
{
nlookups = connectable_count;
do_connectable (argv[1], synchronous, connectable_count);
}
else
{
if (synchronous)
start_sync_lookups (argv + 1, argc - 1);
else
start_async_lookups (argv + 1, argc - 1);
}
g_main_loop_run (loop);
g_main_loop_unref (loop);
#ifdef G_OS_UNIX
g_source_remove (watch);
#endif
g_object_unref (cancellable);
return 0;
}