mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 08:22:16 +01:00 
			
		
		
		
	If `async_cancel()` was invoked, it would remove the IO watch source, which would cause the `g_source_remove()` call at the end of `main()` to warn about an unknown source ID. Fix that by handling the source as a pointer instead of a handle. Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
		
			
				
	
	
		
			805 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			805 lines
		
	
	
		
			20 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.
 | |
|  *
 | |
|  * SPDX-License-Identifier: LGPL-2.1-or-later
 | |
|  *
 | |
|  * 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.1 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, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| #include <glib.h>
 | |
| #include "glibintl.h"
 | |
| 
 | |
| #include <signal.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #ifdef G_OS_UNIX
 | |
| #include <unistd.h>
 | |
| #endif
 | |
| 
 | |
| #include <gio/gio.h>
 | |
| 
 | |
| static GResolver *resolver;
 | |
| static GCancellable *cancellable;
 | |
| static GMainLoop *loop;
 | |
| static int nlookups = 0;
 | |
| static gboolean synchronous = FALSE;
 | |
| static guint connectable_count = 0;
 | |
| static GResolverRecordType record_type = 0;
 | |
| static gint timeout_ms = 0;
 | |
| 
 | |
| static G_NORETURN void
 | |
| usage (void)
 | |
| {
 | |
| 	fprintf (stderr, "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n");
 | |
| 	fprintf (stderr, "Usage: resolver [-s] [-t MX|TXT|NS|SOA|SRV] rrname ...\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");
 | |
| 	fprintf (stderr, "       Use -t with MX, TXT, NS, SOA or SRV to look up DNS records of those types.\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),
 | |
| 		  (guint)g_srv_target_get_port (t->data),
 | |
| 		  (guint)g_srv_target_get_priority (t->data),
 | |
| 		  (guint)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
 | |
| print_resolved_mx (const char *rrname,
 | |
|                    GList      *records,
 | |
|                    GError     *error)
 | |
| {
 | |
|   const gchar *hostname;
 | |
|   guint16 priority;
 | |
|   GList *t;
 | |
| 
 | |
|   G_LOCK (response);
 | |
|   printf ("Domain: %s\n", rrname);
 | |
|   if (error)
 | |
|     {
 | |
|       printf ("Error: %s\n", error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
|   else if (!records)
 | |
|     {
 | |
|       printf ("no MX records\n");
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       for (t = records; t; t = t->next)
 | |
|         {
 | |
|           g_variant_get (t->data, "(q&s)", &priority, &hostname);
 | |
|           printf ("%s (pri %u)\n", hostname, (guint)priority);
 | |
|           g_variant_unref (t->data);
 | |
|         }
 | |
|       g_list_free (records);
 | |
|     }
 | |
|   printf ("\n");
 | |
| 
 | |
|   done_lookup ();
 | |
|   G_UNLOCK (response);
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_resolved_txt (const char *rrname,
 | |
|                     GList      *records,
 | |
|                     GError     *error)
 | |
| {
 | |
|   const gchar **contents;
 | |
|   GList *t;
 | |
|   gint i;
 | |
| 
 | |
|   G_LOCK (response);
 | |
|   printf ("Domain: %s\n", rrname);
 | |
|   if (error)
 | |
|     {
 | |
|       printf ("Error: %s\n", error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
|   else if (!records)
 | |
|     {
 | |
|       printf ("no TXT records\n");
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       for (t = records; t; t = t->next)
 | |
|         {
 | |
|           if (t != records)
 | |
|             printf ("\n");
 | |
|           g_variant_get (t->data, "(^a&s)", &contents);
 | |
|           for (i = 0; contents[i] != NULL; i++)
 | |
|             printf ("%s\n", contents[i]);
 | |
|           g_variant_unref (t->data);
 | |
|           g_free (contents);
 | |
|         }
 | |
|       g_list_free (records);
 | |
|     }
 | |
|   printf ("\n");
 | |
| 
 | |
|   done_lookup ();
 | |
|   G_UNLOCK (response);
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_resolved_srv (const char *rrname,
 | |
|                     GList      *records,
 | |
|                     GError     *error)
 | |
| {
 | |
|   G_LOCK (response);
 | |
|   printf ("Domain: %s\n", rrname);
 | |
|   if (error)
 | |
|     {
 | |
|       printf ("Error: %s\n", error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
|   else if (!records)
 | |
|     {
 | |
|       printf ("no SRV records\n");
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       GList *t;
 | |
| 
 | |
|       for (t = records; t != NULL; t = t->next)
 | |
|         {
 | |
|           guint16 priority, weight, port;
 | |
|           const gchar *target;
 | |
| 
 | |
|           g_variant_get (t->data, "(qqq&s)", &priority, &weight, &port, &target);
 | |
| 
 | |
|           printf ("%s (priority %u, weight %u, port %u)\n",
 | |
|                   target, (guint) priority, (guint) weight, (guint) port);
 | |
|           g_variant_unref (t->data);
 | |
|         }
 | |
| 
 | |
|       g_list_free (records);
 | |
|     }
 | |
|   printf ("\n");
 | |
| 
 | |
|   done_lookup ();
 | |
|   G_UNLOCK (response);
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_resolved_soa (const char *rrname,
 | |
|                     GList      *records,
 | |
|                     GError     *error)
 | |
| {
 | |
|   GList *t;
 | |
|   const gchar *primary_ns;
 | |
|   const gchar *administrator;
 | |
|   guint32 serial, refresh, retry, expire, ttl;
 | |
| 
 | |
|   G_LOCK (response);
 | |
|   printf ("Zone: %s\n", rrname);
 | |
|   if (error)
 | |
|     {
 | |
|       printf ("Error: %s\n", error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
|   else if (!records)
 | |
|     {
 | |
|       printf ("no SOA records\n");
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       for (t = records; t; t = t->next)
 | |
|         {
 | |
|           g_variant_get (t->data, "(&s&suuuuu)", &primary_ns, &administrator,
 | |
|                          &serial, &refresh, &retry, &expire, &ttl);
 | |
|           printf ("%s %s (serial %u, refresh %u, retry %u, expire %u, ttl %u)\n",
 | |
|                   primary_ns, administrator, (guint)serial, (guint)refresh,
 | |
|                   (guint)retry, (guint)expire, (guint)ttl);
 | |
|           g_variant_unref (t->data);
 | |
|         }
 | |
|       g_list_free (records);
 | |
|     }
 | |
|   printf ("\n");
 | |
| 
 | |
|   done_lookup ();
 | |
|   G_UNLOCK (response);
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_resolved_ns (const char *rrname,
 | |
|                     GList      *records,
 | |
|                     GError     *error)
 | |
| {
 | |
|   GList *t;
 | |
|   const gchar *hostname;
 | |
| 
 | |
|   G_LOCK (response);
 | |
|   printf ("Zone: %s\n", rrname);
 | |
|   if (error)
 | |
|     {
 | |
|       printf ("Error: %s\n", error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
|   else if (!records)
 | |
|     {
 | |
|       printf ("no NS records\n");
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       for (t = records; t; t = t->next)
 | |
|         {
 | |
|           g_variant_get (t->data, "(&s)", &hostname);
 | |
|           printf ("%s\n", hostname);
 | |
|           g_variant_unref (t->data);
 | |
|         }
 | |
|       g_list_free (records);
 | |
|     }
 | |
|   printf ("\n");
 | |
| 
 | |
|   done_lookup ();
 | |
|   G_UNLOCK (response);
 | |
| }
 | |
| 
 | |
| static void
 | |
| lookup_one_sync (const char *arg)
 | |
| {
 | |
|   GError *error = NULL;
 | |
| 
 | |
|   if (record_type != 0)
 | |
|     {
 | |
|       GList *records;
 | |
| 
 | |
|       records = g_resolver_lookup_records (resolver, arg, record_type, cancellable, &error);
 | |
|       switch (record_type)
 | |
|       {
 | |
|         case G_RESOLVER_RECORD_MX:
 | |
|           print_resolved_mx (arg, records, error);
 | |
|           break;
 | |
|         case G_RESOLVER_RECORD_SOA:
 | |
|           print_resolved_soa (arg, records, error);
 | |
|           break;
 | |
|         case G_RESOLVER_RECORD_NS:
 | |
|           print_resolved_ns (arg, records, error);
 | |
|           break;
 | |
|         case G_RESOLVER_RECORD_TXT:
 | |
|           print_resolved_txt (arg, records, error);
 | |
|           break;
 | |
|         case G_RESOLVER_RECORD_SRV:
 | |
|           print_resolved_srv (arg, records, error);
 | |
|           break;
 | |
|         default:
 | |
|           g_warn_if_reached ();
 | |
|           break;
 | |
|       }
 | |
|     }
 | |
|   else 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++)
 | |
|     {
 | |
|       GThread *thread;
 | |
|       thread = g_thread_new ("lookup", lookup_thread, argv[i]);
 | |
|       g_thread_unref (thread);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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
 | |
| lookup_records_callback (GObject      *source,
 | |
|                          GAsyncResult *result,
 | |
|                          gpointer      user_data)
 | |
| {
 | |
|   const char *arg = user_data;
 | |
|   GError *error = NULL;
 | |
|   GList *records;
 | |
| 
 | |
|   records = g_resolver_lookup_records_finish (resolver, result, &error);
 | |
| 
 | |
|   switch (record_type)
 | |
|   {
 | |
|     case G_RESOLVER_RECORD_MX:
 | |
|       print_resolved_mx (arg, records, error);
 | |
|       break;
 | |
|     case G_RESOLVER_RECORD_SOA:
 | |
|       print_resolved_soa (arg, records, error);
 | |
|       break;
 | |
|     case G_RESOLVER_RECORD_NS:
 | |
|       print_resolved_ns (arg, records, error);
 | |
|       break;
 | |
|     case G_RESOLVER_RECORD_TXT:
 | |
|       print_resolved_txt (arg, records, error);
 | |
|       break;
 | |
|     case G_RESOLVER_RECORD_SRV:
 | |
|       print_resolved_srv (arg, records, error);
 | |
|       break;
 | |
|     default:
 | |
|       g_warn_if_reached ();
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| start_async_lookups (char **argv, int argc)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < argc; i++)
 | |
|     {
 | |
|       if (record_type != 0)
 | |
| 	{
 | |
| 	  g_resolver_lookup_records_async (resolver, argv[i], record_type,
 | |
| 	                                   cancellable, lookup_records_callback, argv[i]);
 | |
| 	}
 | |
|       else 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 synch, 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 (synch)
 | |
|         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 G_SOURCE_REMOVE;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| static gboolean
 | |
| record_type_arg (const gchar *option_name,
 | |
|                  const gchar *value,
 | |
|                  gpointer data,
 | |
|                  GError **error)
 | |
| {
 | |
|   if (g_ascii_strcasecmp (value, "MX") == 0) {
 | |
|     record_type = G_RESOLVER_RECORD_MX;
 | |
|   } else if (g_ascii_strcasecmp (value, "TXT") == 0) {
 | |
|     record_type = G_RESOLVER_RECORD_TXT;
 | |
|   } else if (g_ascii_strcasecmp (value, "SOA") == 0) {
 | |
|     record_type = G_RESOLVER_RECORD_SOA;
 | |
|   } else if (g_ascii_strcasecmp (value, "NS") == 0) {
 | |
|     record_type = G_RESOLVER_RECORD_NS;
 | |
|   } else if (g_ascii_strcasecmp (value, "SRV") == 0) {
 | |
|     record_type = G_RESOLVER_RECORD_SRV;
 | |
|   } else {
 | |
|       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
 | |
|                    "Specify MX, TXT, NS, SOA or SRV for the special record lookup types");
 | |
|       return FALSE;
 | |
|   }
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static const GOptionEntry option_entries[] = {
 | |
|   { "synchronous", 's', 0, G_OPTION_ARG_NONE, &synchronous, "Synchronous connections", NULL },
 | |
|   { "connectable", 'c', 0, G_OPTION_ARG_INT, &connectable_count, "Connectable count", "C" },
 | |
|   { "special-type", 't', 0, G_OPTION_ARG_CALLBACK, record_type_arg, "Record type like MX, TXT, NS or SOA", "RR" },
 | |
|   { "timeout", 0, 0, G_OPTION_ARG_INT, &timeout_ms, "Timeout (ms)", "ms" },
 | |
|   G_OPTION_ENTRY_NULL,
 | |
| };
 | |
| 
 | |
| int
 | |
| main (int argc, char **argv)
 | |
| {
 | |
|   GOptionContext *context;
 | |
|   GError *error = NULL;
 | |
| #ifdef G_OS_UNIX
 | |
|   GIOChannel *chan;
 | |
|   GSource *watch_source = NULL;
 | |
| #endif
 | |
| 
 | |
|   context = g_option_context_new ("lookups ...");
 | |
|   g_option_context_add_main_entries (context, option_entries, NULL);
 | |
|   if (!g_option_context_parse (context, &argc, &argv, &error))
 | |
|     {
 | |
|       g_printerr ("%s\n", error->message);
 | |
|       g_error_free (error);
 | |
|       usage();
 | |
|     }
 | |
| 
 | |
|   if (argc < 2 || (argc > 2 && connectable_count))
 | |
|     usage ();
 | |
| 
 | |
|   resolver = g_resolver_get_default ();
 | |
| 
 | |
|   if (timeout_ms != 0)
 | |
|     g_resolver_set_timeout (resolver, timeout_ms);
 | |
| 
 | |
|   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_source = g_io_create_watch (chan, G_IO_IN);
 | |
|   g_source_set_callback (watch_source, (GSourceFunc) async_cancel, cancellable, NULL);
 | |
|   g_source_attach (watch_source, NULL);
 | |
|   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_destroy (watch_source);
 | |
|   g_clear_pointer (&watch_source, g_source_unref);
 | |
| #endif
 | |
|   g_object_unref (cancellable);
 | |
|   g_option_context_free (context);
 | |
| 
 | |
|   return 0;
 | |
| }
 |