1  /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
       2  
       3  /* GIO - GLib Input, Output and Streaming Library
       4   * 
       5   * Copyright (C) 2008 Red Hat, Inc.
       6   *
       7   * SPDX-License-Identifier: LGPL-2.1-or-later
       8   *
       9   * This library is free software; you can redistribute it and/or
      10   * modify it under the terms of the GNU Lesser General Public
      11   * License as published by the Free Software Foundation; either
      12   * version 2.1 of the License, or (at your option) any later version.
      13   *
      14   * This library is distributed in the hope that it will be useful,
      15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17   * Lesser General Public License for more details.
      18   *
      19   * You should have received a copy of the GNU Lesser General
      20   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      21   */
      22  
      23  #include "config.h"
      24  #include <glib.h>
      25  #include "glibintl.h"
      26  
      27  #include <signal.h>
      28  #include <stdio.h>
      29  #include <stdlib.h>
      30  #include <string.h>
      31  #ifdef G_OS_UNIX
      32  #include <unistd.h>
      33  #endif
      34  
      35  #include <gio/gio.h>
      36  
      37  static GResolver *resolver;
      38  static GCancellable *cancellable;
      39  static GMainLoop *loop;
      40  static int nlookups = 0;
      41  static gboolean synchronous = FALSE;
      42  static guint connectable_count = 0;
      43  static GResolverRecordType record_type = 0;
      44  static gint timeout_ms = 0;
      45  
      46  static G_NORETURN void
      47  usage (void)
      48  {
      49  	fprintf (stderr, "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n");
      50  	fprintf (stderr, "Usage: resolver [-s] [-t MX|TXT|NS|SOA|SRV] rrname ...\n");
      51  	fprintf (stderr, "       resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
      52  	fprintf (stderr, "       Use -s to do synchronous lookups.\n");
      53  	fprintf (stderr, "       Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n");
      54  	fprintf (stderr, "       The given NUMBER determines how many times the connectable will be enumerated.\n");
      55  	fprintf (stderr, "       Use -t with MX, TXT, NS, SOA or SRV to look up DNS records of those types.\n");
      56  	exit (1);
      57  }
      58  
      59  G_LOCK_DEFINE_STATIC (response);
      60  
      61  static void
      62  done_lookup (void)
      63  {
      64    nlookups--;
      65    if (nlookups == 0)
      66      {
      67        /* In the sync case we need to make sure we don't call
      68         * g_main_loop_quit before the loop is actually running...
      69         */
      70        g_idle_add ((GSourceFunc)g_main_loop_quit, loop);
      71      }
      72  }
      73  
      74  static void
      75  print_resolved_name (const char *phys,
      76                       char       *name,
      77                       GError     *error)
      78  {
      79    G_LOCK (response);
      80    printf ("Address: %s\n", phys);
      81    if (error)
      82      {
      83        printf ("Error:   %s\n", error->message);
      84        g_error_free (error);
      85      }
      86    else
      87      {
      88        printf ("Name:    %s\n", name);
      89        g_free (name);
      90      }
      91    printf ("\n");
      92  
      93    done_lookup ();
      94    G_UNLOCK (response);
      95  }
      96  
      97  static void
      98  print_resolved_addresses (const char *name,
      99                            GList      *addresses,
     100  			  GError     *error)
     101  {
     102    char *phys;
     103    GList *a;
     104  
     105    G_LOCK (response);
     106    printf ("Name:    %s\n", name);
     107    if (error)
     108      {
     109        printf ("Error:   %s\n", error->message);
     110        g_error_free (error);
     111      }
     112    else
     113      {
     114        for (a = addresses; a; a = a->next)
     115  	{
     116  	  phys = g_inet_address_to_string (a->data);
     117  	  printf ("Address: %s\n", phys);
     118  	  g_free (phys);
     119            g_object_unref (a->data);
     120  	}
     121        g_list_free (addresses);
     122      }
     123    printf ("\n");
     124  
     125    done_lookup ();
     126    G_UNLOCK (response);
     127  }
     128  
     129  static void
     130  print_resolved_service (const char *service,
     131                          GList      *targets,
     132  			GError     *error)
     133  {
     134    GList *t;  
     135  
     136    G_LOCK (response);
     137    printf ("Service: %s\n", service);
     138    if (error)
     139      {
     140        printf ("Error: %s\n", error->message);
     141        g_error_free (error);
     142      }
     143    else
     144      {
     145        for (t = targets; t; t = t->next)
     146  	{
     147  	  printf ("%s:%u (pri %u, weight %u)\n",
     148  		  g_srv_target_get_hostname (t->data),
     149  		  (guint)g_srv_target_get_port (t->data),
     150  		  (guint)g_srv_target_get_priority (t->data),
     151  		  (guint)g_srv_target_get_weight (t->data));
     152            g_srv_target_free (t->data);
     153  	}
     154        g_list_free (targets);
     155      }
     156    printf ("\n");
     157  
     158    done_lookup ();
     159    G_UNLOCK (response);
     160  }
     161  
     162  static void
     163  print_resolved_mx (const char *rrname,
     164                     GList      *records,
     165                     GError     *error)
     166  {
     167    const gchar *hostname;
     168    guint16 priority;
     169    GList *t;
     170  
     171    G_LOCK (response);
     172    printf ("Domain: %s\n", rrname);
     173    if (error)
     174      {
     175        printf ("Error: %s\n", error->message);
     176        g_error_free (error);
     177      }
     178    else if (!records)
     179      {
     180        printf ("no MX records\n");
     181      }
     182    else
     183      {
     184        for (t = records; t; t = t->next)
     185          {
     186            g_variant_get (t->data, "(q&s)", &priority, &hostname);
     187            printf ("%s (pri %u)\n", hostname, (guint)priority);
     188            g_variant_unref (t->data);
     189          }
     190        g_list_free (records);
     191      }
     192    printf ("\n");
     193  
     194    done_lookup ();
     195    G_UNLOCK (response);
     196  }
     197  
     198  static void
     199  print_resolved_txt (const char *rrname,
     200                      GList      *records,
     201                      GError     *error)
     202  {
     203    const gchar **contents;
     204    GList *t;
     205    gint i;
     206  
     207    G_LOCK (response);
     208    printf ("Domain: %s\n", rrname);
     209    if (error)
     210      {
     211        printf ("Error: %s\n", error->message);
     212        g_error_free (error);
     213      }
     214    else if (!records)
     215      {
     216        printf ("no TXT records\n");
     217      }
     218    else
     219      {
     220        for (t = records; t; t = t->next)
     221          {
     222            if (t != records)
     223              printf ("\n");
     224            g_variant_get (t->data, "(^a&s)", &contents);
     225            for (i = 0; contents[i] != NULL; i++)
     226              printf ("%s\n", contents[i]);
     227            g_variant_unref (t->data);
     228            g_free (contents);
     229          }
     230        g_list_free (records);
     231      }
     232    printf ("\n");
     233  
     234    done_lookup ();
     235    G_UNLOCK (response);
     236  }
     237  
     238  static void
     239  print_resolved_srv (const char *rrname,
     240                      GList      *records,
     241                      GError     *error)
     242  {
     243    G_LOCK (response);
     244    printf ("Domain: %s\n", rrname);
     245    if (error)
     246      {
     247        printf ("Error: %s\n", error->message);
     248        g_error_free (error);
     249      }
     250    else if (!records)
     251      {
     252        printf ("no SRV records\n");
     253      }
     254    else
     255      {
     256        GList *t;
     257  
     258        for (t = records; t != NULL; t = t->next)
     259          {
     260            guint16 priority, weight, port;
     261            const gchar *target;
     262  
     263            g_variant_get (t->data, "(qqq&s)", &priority, &weight, &port, &target);
     264  
     265            printf ("%s (priority %u, weight %u, port %u)\n",
     266                    target, (guint) priority, (guint) weight, (guint) port);
     267            g_variant_unref (t->data);
     268          }
     269  
     270        g_list_free (records);
     271      }
     272    printf ("\n");
     273  
     274    done_lookup ();
     275    G_UNLOCK (response);
     276  }
     277  
     278  static void
     279  print_resolved_soa (const char *rrname,
     280                      GList      *records,
     281                      GError     *error)
     282  {
     283    GList *t;
     284    const gchar *primary_ns;
     285    const gchar *administrator;
     286    guint32 serial, refresh, retry, expire, ttl;
     287  
     288    G_LOCK (response);
     289    printf ("Zone: %s\n", rrname);
     290    if (error)
     291      {
     292        printf ("Error: %s\n", error->message);
     293        g_error_free (error);
     294      }
     295    else if (!records)
     296      {
     297        printf ("no SOA records\n");
     298      }
     299    else
     300      {
     301        for (t = records; t; t = t->next)
     302          {
     303            g_variant_get (t->data, "(&s&suuuuu)", &primary_ns, &administrator,
     304                           &serial, &refresh, &retry, &expire, &ttl);
     305            printf ("%s %s (serial %u, refresh %u, retry %u, expire %u, ttl %u)\n",
     306                    primary_ns, administrator, (guint)serial, (guint)refresh,
     307                    (guint)retry, (guint)expire, (guint)ttl);
     308            g_variant_unref (t->data);
     309          }
     310        g_list_free (records);
     311      }
     312    printf ("\n");
     313  
     314    done_lookup ();
     315    G_UNLOCK (response);
     316  }
     317  
     318  static void
     319  print_resolved_ns (const char *rrname,
     320                      GList      *records,
     321                      GError     *error)
     322  {
     323    GList *t;
     324    const gchar *hostname;
     325  
     326    G_LOCK (response);
     327    printf ("Zone: %s\n", rrname);
     328    if (error)
     329      {
     330        printf ("Error: %s\n", error->message);
     331        g_error_free (error);
     332      }
     333    else if (!records)
     334      {
     335        printf ("no NS records\n");
     336      }
     337    else
     338      {
     339        for (t = records; t; t = t->next)
     340          {
     341            g_variant_get (t->data, "(&s)", &hostname);
     342            printf ("%s\n", hostname);
     343            g_variant_unref (t->data);
     344          }
     345        g_list_free (records);
     346      }
     347    printf ("\n");
     348  
     349    done_lookup ();
     350    G_UNLOCK (response);
     351  }
     352  
     353  static void
     354  lookup_one_sync (const char *arg)
     355  {
     356    GError *error = NULL;
     357  
     358    if (record_type != 0)
     359      {
     360        GList *records;
     361  
     362        records = g_resolver_lookup_records (resolver, arg, record_type, cancellable, &error);
     363        switch (record_type)
     364        {
     365          case G_RESOLVER_RECORD_MX:
     366            print_resolved_mx (arg, records, error);
     367            break;
     368          case G_RESOLVER_RECORD_SOA:
     369            print_resolved_soa (arg, records, error);
     370            break;
     371          case G_RESOLVER_RECORD_NS:
     372            print_resolved_ns (arg, records, error);
     373            break;
     374          case G_RESOLVER_RECORD_TXT:
     375            print_resolved_txt (arg, records, error);
     376            break;
     377          case G_RESOLVER_RECORD_SRV:
     378            print_resolved_srv (arg, records, error);
     379            break;
     380          default:
     381            g_warn_if_reached ();
     382            break;
     383        }
     384      }
     385    else if (strchr (arg, '/'))
     386      {
     387        GList *targets;
     388        /* service/protocol/domain */
     389        char **parts = g_strsplit (arg, "/", 3);
     390  
     391        if (!parts || !parts[2])
     392  	usage ();
     393  
     394        targets = g_resolver_lookup_service (resolver,
     395                                             parts[0], parts[1], parts[2],
     396                                             cancellable, &error);
     397        print_resolved_service (arg, targets, error);
     398      }
     399    else if (g_hostname_is_ip_address (arg))
     400      {
     401        GInetAddress *addr = g_inet_address_new_from_string (arg);
     402        char *name;
     403  
     404        name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
     405        print_resolved_name (arg, name, error);
     406        g_object_unref (addr);
     407      }
     408    else
     409      {
     410        GList *addresses;
     411  
     412        addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
     413        print_resolved_addresses (arg, addresses, error);
     414      }
     415  }
     416  
     417  static gpointer
     418  lookup_thread (gpointer arg)
     419  {
     420    lookup_one_sync (arg);
     421    return NULL;
     422  }
     423  
     424  static void
     425  start_sync_lookups (char **argv, int argc)
     426  {
     427    int i;
     428  
     429    for (i = 0; i < argc; i++)
     430      {
     431        GThread *thread;
     432        thread = g_thread_new ("lookup", lookup_thread, argv[i]);
     433        g_thread_unref (thread);
     434      }
     435  }
     436  
     437  static void
     438  lookup_by_addr_callback (GObject *source, GAsyncResult *result,
     439                           gpointer user_data)
     440  {
     441    const char *phys = user_data;
     442    GError *error = NULL;
     443    char *name;
     444  
     445    name = g_resolver_lookup_by_address_finish (resolver, result, &error);
     446    print_resolved_name (phys, name, error);
     447  }
     448  
     449  static void
     450  lookup_by_name_callback (GObject *source, GAsyncResult *result,
     451                           gpointer user_data)
     452  {
     453    const char *name = user_data;
     454    GError *error = NULL;
     455    GList *addresses;
     456  
     457    addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
     458    print_resolved_addresses (name, addresses, error);
     459  }
     460  
     461  static void
     462  lookup_service_callback (GObject *source, GAsyncResult *result,
     463  			 gpointer user_data)
     464  {
     465    const char *service = user_data;
     466    GError *error = NULL;
     467    GList *targets;
     468  
     469    targets = g_resolver_lookup_service_finish (resolver, result, &error);
     470    print_resolved_service (service, targets, error);
     471  }
     472  
     473  static void
     474  lookup_records_callback (GObject      *source,
     475                           GAsyncResult *result,
     476                           gpointer      user_data)
     477  {
     478    const char *arg = user_data;
     479    GError *error = NULL;
     480    GList *records;
     481  
     482    records = g_resolver_lookup_records_finish (resolver, result, &error);
     483  
     484    switch (record_type)
     485    {
     486      case G_RESOLVER_RECORD_MX:
     487        print_resolved_mx (arg, records, error);
     488        break;
     489      case G_RESOLVER_RECORD_SOA:
     490        print_resolved_soa (arg, records, error);
     491        break;
     492      case G_RESOLVER_RECORD_NS:
     493        print_resolved_ns (arg, records, error);
     494        break;
     495      case G_RESOLVER_RECORD_TXT:
     496        print_resolved_txt (arg, records, error);
     497        break;
     498      case G_RESOLVER_RECORD_SRV:
     499        print_resolved_srv (arg, records, error);
     500        break;
     501      default:
     502        g_warn_if_reached ();
     503        break;
     504    }
     505  }
     506  
     507  static void
     508  start_async_lookups (char **argv, int argc)
     509  {
     510    int i;
     511  
     512    for (i = 0; i < argc; i++)
     513      {
     514        if (record_type != 0)
     515  	{
     516  	  g_resolver_lookup_records_async (resolver, argv[i], record_type,
     517  	                                   cancellable, lookup_records_callback, argv[i]);
     518  	}
     519        else if (strchr (argv[i], '/'))
     520  	{
     521  	  /* service/protocol/domain */
     522  	  char **parts = g_strsplit (argv[i], "/", 3);
     523  
     524  	  if (!parts || !parts[2])
     525  	    usage ();
     526  
     527  	  g_resolver_lookup_service_async (resolver,
     528  					   parts[0], parts[1], parts[2],
     529  					   cancellable,
     530  					   lookup_service_callback, argv[i]);
     531  	}
     532        else if (g_hostname_is_ip_address (argv[i]))
     533  	{
     534            GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
     535  
     536  	  g_resolver_lookup_by_address_async (resolver, addr, cancellable,
     537                                                lookup_by_addr_callback, argv[i]);
     538  	  g_object_unref (addr);
     539  	}
     540        else
     541  	{
     542  	  g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
     543                                             lookup_by_name_callback,
     544                                             argv[i]);
     545  	}
     546  
     547        /* Stress-test the reloading code */
     548        g_signal_emit_by_name (resolver, "reload");
     549      }
     550  }
     551  
     552  static void
     553  print_connectable_sockaddr (GSocketAddress *sockaddr,
     554                              GError         *error)
     555  {
     556    char *phys;
     557  
     558    if (error)
     559      {
     560        printf ("Error:   %s\n", error->message);
     561        g_error_free (error);
     562      }
     563    else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
     564      {
     565        printf ("Error:   Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
     566        g_object_unref (sockaddr);
     567      }
     568    else
     569      {
     570        GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr);
     571        phys = g_inet_address_to_string (g_inet_socket_address_get_address (isa));
     572        printf ("Address: %s%s%s:%d\n",
     573                strchr (phys, ':') ? "[" : "", phys, strchr (phys, ':') ? "]" : "",
     574                g_inet_socket_address_get_port (isa));
     575        g_free (phys);
     576        g_object_unref (sockaddr);
     577      }
     578  }
     579  
     580  static void
     581  do_sync_connectable (GSocketAddressEnumerator *enumerator)
     582  {
     583    GSocketAddress *sockaddr;
     584    GError *error = NULL;
     585  
     586    while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
     587      print_connectable_sockaddr (sockaddr, error);
     588  
     589    g_object_unref (enumerator);
     590    done_lookup ();
     591  }
     592  
     593  static void do_async_connectable (GSocketAddressEnumerator *enumerator);
     594  
     595  static void
     596  got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
     597  {
     598    GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
     599    GSocketAddress *sockaddr;
     600    GError *error = NULL;
     601  
     602    sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
     603    if (sockaddr || error)
     604      print_connectable_sockaddr (sockaddr, error);
     605    if (sockaddr)
     606      do_async_connectable (enumerator);
     607    else
     608      {
     609        g_object_unref (enumerator);
     610        done_lookup ();
     611      }
     612  }
     613  
     614  static void
     615  do_async_connectable (GSocketAddressEnumerator *enumerator)
     616  {
     617    g_socket_address_enumerator_next_async (enumerator, cancellable,
     618                                            got_next_async, NULL);
     619  }
     620  
     621  static void
     622  do_connectable (const char *arg, gboolean synch, guint count)
     623  {
     624    char **parts;
     625    GSocketConnectable *connectable;
     626    GSocketAddressEnumerator *enumerator;
     627  
     628    if (strchr (arg, '/'))
     629      {
     630        /* service/protocol/domain */
     631        parts = g_strsplit (arg, "/", 3);
     632        if (!parts || !parts[2])
     633  	usage ();
     634  
     635        connectable = g_network_service_new (parts[0], parts[1], parts[2]);
     636      }
     637    else
     638      {
     639        guint16 port;
     640  
     641        parts = g_strsplit (arg, ":", 2);
     642        if (parts && parts[1])
     643  	{
     644  	  arg = parts[0];
     645  	  port = strtoul (parts[1], NULL, 10);
     646  	}
     647        else
     648  	port = 0;
     649  
     650        if (g_hostname_is_ip_address (arg))
     651  	{
     652  	  GInetAddress *addr = g_inet_address_new_from_string (arg);
     653  	  GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
     654  
     655  	  g_object_unref (addr);
     656  	  connectable = G_SOCKET_CONNECTABLE (sockaddr);
     657  	}
     658        else
     659          connectable = g_network_address_new (arg, port);
     660      }
     661  
     662    while (count--)
     663      {
     664        enumerator = g_socket_connectable_enumerate (connectable);
     665  
     666        if (synch)
     667          do_sync_connectable (enumerator);
     668        else
     669          do_async_connectable (enumerator);
     670      }
     671    
     672    g_object_unref (connectable);
     673  }
     674  
     675  #ifdef G_OS_UNIX
     676  static int cancel_fds[2];
     677  
     678  static void
     679  interrupted (int sig)
     680  {
     681    gssize c;
     682  
     683    signal (SIGINT, SIG_DFL);
     684    c = write (cancel_fds[1], "x", 1);
     685    g_assert_cmpint(c, ==, 1);
     686  }
     687  
     688  static gboolean
     689  async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
     690  {
     691    g_cancellable_cancel (cancel);
     692    return G_SOURCE_REMOVE;
     693  }
     694  #endif
     695  
     696  
     697  static gboolean
     698  record_type_arg (const gchar *option_name,
     699                   const gchar *value,
     700                   gpointer data,
     701                   GError **error)
     702  {
     703    if (g_ascii_strcasecmp (value, "MX") == 0) {
     704      record_type = G_RESOLVER_RECORD_MX;
     705    } else if (g_ascii_strcasecmp (value, "TXT") == 0) {
     706      record_type = G_RESOLVER_RECORD_TXT;
     707    } else if (g_ascii_strcasecmp (value, "SOA") == 0) {
     708      record_type = G_RESOLVER_RECORD_SOA;
     709    } else if (g_ascii_strcasecmp (value, "NS") == 0) {
     710      record_type = G_RESOLVER_RECORD_NS;
     711    } else if (g_ascii_strcasecmp (value, "SRV") == 0) {
     712      record_type = G_RESOLVER_RECORD_SRV;
     713    } else {
     714        g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
     715                     "Specify MX, TXT, NS, SOA or SRV for the special record lookup types");
     716        return FALSE;
     717    }
     718  
     719    return TRUE;
     720  }
     721  
     722  static const GOptionEntry option_entries[] = {
     723    { "synchronous", 's', 0, G_OPTION_ARG_NONE, &synchronous, "Synchronous connections", NULL },
     724    { "connectable", 'c', 0, G_OPTION_ARG_INT, &connectable_count, "Connectable count", "C" },
     725    { "special-type", 't', 0, G_OPTION_ARG_CALLBACK, record_type_arg, "Record type like MX, TXT, NS or SOA", "RR" },
     726    { "timeout", 0, 0, G_OPTION_ARG_INT, &timeout_ms, "Timeout (ms)", "ms" },
     727    G_OPTION_ENTRY_NULL,
     728  };
     729  
     730  int
     731  main (int argc, char **argv)
     732  {
     733    GOptionContext *context;
     734    GError *error = NULL;
     735  #ifdef G_OS_UNIX
     736    GIOChannel *chan;
     737    GSource *watch_source = NULL;
     738  #endif
     739  
     740    context = g_option_context_new ("lookups ...");
     741    g_option_context_add_main_entries (context, option_entries, NULL);
     742    if (!g_option_context_parse (context, &argc, &argv, &error))
     743      {
     744        g_printerr ("%s\n", error->message);
     745        g_error_free (error);
     746        usage();
     747      }
     748  
     749    if (argc < 2 || (argc > 2 && connectable_count))
     750      usage ();
     751  
     752    resolver = g_resolver_get_default ();
     753  
     754    if (timeout_ms != 0)
     755      g_resolver_set_timeout (resolver, timeout_ms);
     756  
     757    cancellable = g_cancellable_new ();
     758  
     759  #ifdef G_OS_UNIX
     760    /* Set up cancellation; we want to cancel if the user ^C's the
     761     * program, but we can't cancel directly from an interrupt.
     762     */
     763    signal (SIGINT, interrupted);
     764  
     765    if (pipe (cancel_fds) == -1)
     766      {
     767        perror ("pipe");
     768        exit (1);
     769      }
     770    chan = g_io_channel_unix_new (cancel_fds[0]);
     771    watch_source = g_io_create_watch (chan, G_IO_IN);
     772    g_source_set_callback (watch_source, (GSourceFunc) async_cancel, cancellable, NULL);
     773    g_source_attach (watch_source, NULL);
     774    g_io_channel_unref (chan);
     775  #endif
     776  
     777    nlookups = argc - 1;
     778    loop = g_main_loop_new (NULL, TRUE);
     779  
     780    if (connectable_count)
     781      {
     782        nlookups = connectable_count;
     783        do_connectable (argv[1], synchronous, connectable_count);
     784      }
     785    else
     786      {
     787        if (synchronous)
     788          start_sync_lookups (argv + 1, argc - 1);
     789        else
     790          start_async_lookups (argv + 1, argc - 1);
     791      }
     792  
     793    g_main_loop_run (loop);
     794    g_main_loop_unref (loop);
     795  
     796  #ifdef G_OS_UNIX
     797    g_source_destroy (watch_source);
     798    g_clear_pointer (&watch_source, g_source_unref);
     799  #endif
     800    g_object_unref (cancellable);
     801    g_option_context_free (context);
     802  
     803    return 0;
     804  }